summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-02-03 09:39:56 -0800
committerPaul Phillips <paulp@improving.org>2012-02-03 09:39:56 -0800
commit7ec4ba5ea921eafb092b05f580ba7381edcd8427 (patch)
tree18d2553403b64ee683536f61b59ce62035305699 /src
parentb4cfd1161e9f549a909d9f563f557c17dbddad00 (diff)
parent58ab20ba9b6a17c81f2f89f31f7265d2ed3b166b (diff)
downloadscala-7ec4ba5ea921eafb092b05f580ba7381edcd8427.tar.gz
scala-7ec4ba5ea921eafb092b05f580ba7381edcd8427.tar.bz2
scala-7ec4ba5ea921eafb092b05f580ba7381edcd8427.zip
Merge branch 'master' into topic/inline
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/internal/Constants.scala2
-rw-r--r--src/compiler/scala/reflect/internal/Flags.scala36
-rw-r--r--src/compiler/scala/reflect/internal/Importers.scala32
-rw-r--r--src/compiler/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/compiler/scala/reflect/internal/Symbols.scala10
-rw-r--r--src/compiler/scala/reflect/internal/Trees.scala2
-rw-r--r--src/compiler/scala/reflect/internal/Types.scala2
-rw-r--r--src/compiler/scala/reflect/runtime/JavaToScala.scala118
-rw-r--r--src/compiler/scala/reflect/runtime/ToolBoxes.scala49
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala107
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala10
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala9
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala100
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala37
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala6
-rw-r--r--src/compiler/scala/tools/nsc/io/Path.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala4
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala36
-rw-r--r--src/compiler/scala/tools/nsc/transform/LiftCode.scala105
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala1307
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala15
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala16
-rw-r--r--src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java10
-rw-r--r--src/library/scala/MatchingStrategy.scala27
-rwxr-xr-xsrc/library/scala/collection/IndexedSeqOptimized.scala2
-rw-r--r--src/library/scala/math/BigDecimal.scala4
-rwxr-xr-xsrc/library/scala/reflect/api/StandardDefinitions.scala7
-rwxr-xr-xsrc/library/scala/reflect/api/Symbols.scala2
-rw-r--r--src/library/scala/reflect/api/Trees.scala3
-rw-r--r--src/partest/scala/tools/partest/utils/CodeTest.scala10
32 files changed, 1184 insertions, 892 deletions
diff --git a/src/compiler/scala/reflect/internal/Constants.scala b/src/compiler/scala/reflect/internal/Constants.scala
index 9c4b2b2245..c328cc49cb 100644
--- a/src/compiler/scala/reflect/internal/Constants.scala
+++ b/src/compiler/scala/reflect/internal/Constants.scala
@@ -45,7 +45,7 @@ trait Constants extends api.Constants {
case x: Char => CharTag
case x: Type => ClassTag
case x: Symbol => EnumTag
- case _ => throw new Error("bad constant value: " + value)
+ case _ => throw new Error("bad constant value: " + value + " of class " + value.getClass)
}
def isByteRange: Boolean = isIntRange && Byte.MinValue <= intValue && intValue <= Byte.MaxValue
diff --git a/src/compiler/scala/reflect/internal/Flags.scala b/src/compiler/scala/reflect/internal/Flags.scala
index 9e4f0431c3..66af92be5f 100644
--- a/src/compiler/scala/reflect/internal/Flags.scala
+++ b/src/compiler/scala/reflect/internal/Flags.scala
@@ -13,7 +13,7 @@ import scala.collection.{ mutable, immutable }
// Parsers/JavaParsers and therefore definitely appear on Modifiers; but the
// absence of /M on the other flags does not imply they aren't.
//
-// Generated by mkFlagsTable() at Mon Oct 11 10:01:09 PDT 2010
+// Generated by mkFlagsTable() at Thu Feb 02 20:31:52 PST 2012
//
// 0: PROTECTED/M
// 1: OVERRIDE/M
@@ -30,7 +30,7 @@ import scala.collection.{ mutable, immutable }
// 12: MUTABLE/M
// 13: PARAM/M
// 14: PACKAGE
-// 15:
+// 15: MACRO/M
// 16: BYNAMEPARAM/M CAPTURED COVARIANT/M
// 17: CONTRAVARIANT/M INCONSTRUCTOR LABEL
// 18: ABSOVERRIDE/M
@@ -59,13 +59,13 @@ import scala.collection.{ mutable, immutable }
// 41: DEFAULTINIT/M
// 42: VBRIDGE
// 43: VARARGS
-// 44:
+// 44: TRIEDCOOKING
// 45:
// 46:
// 47:
// 48:
-// 49: latePRIVATE (eliminated)
-// 50: lateABSTRACT (eliminated)
+// 49:
+// 50:
// 51: lateDEFERRED
// 52: lateFINAL
// 53: lateMETHOD
@@ -74,10 +74,10 @@ import scala.collection.{ mutable, immutable }
// 56: notPROTECTED
// 57: notOVERRIDE
// 58: notPRIVATE
-// 59: notABSTRACT (eliminated)
-// 60: notDEFERRED (eliminated)
-// 61: notFINAL (eliminated)
-// 62: notMETHOD (eliminated)
+// 59:
+// 60:
+// 61:
+// 62:
// 63:
/** Flags set on Modifiers instances in the parsing stage.
@@ -337,7 +337,7 @@ class Flags extends ModifierFlags {
// ------ displaying flags --------------------------------------------------------
- // Generated by mkFlagToStringMethod() at Mon Oct 11 10:12:36 PDT 2010
+ // Generated by mkFlagToStringMethod() at Thu Feb 02 20:31:52 PST 2012
@annotation.switch override def flagToString(flag: Long): String = flag match {
case PROTECTED => "protected" // (1L << 0)
case OVERRIDE => "override" // (1L << 1)
@@ -354,7 +354,7 @@ class Flags extends ModifierFlags {
case MUTABLE => "<mutable>" // (1L << 12)
case PARAM => "<param>" // (1L << 13)
case PACKAGE => "<package>" // (1L << 14)
- case MACRO => "macro" // (1L << 15)
+ case MACRO => "<macro>" // (1L << 15)
case BYNAMEPARAM => "<bynameparam/captured/covariant>" // (1L << 16)
case CONTRAVARIANT => "<contravariant/inconstructor/label>" // (1L << 17)
case ABSOVERRIDE => "absoverride" // (1L << 18)
@@ -383,13 +383,13 @@ class Flags extends ModifierFlags {
case DEFAULTINIT => "<defaultinit>" // (1L << 41)
case VBRIDGE => "<vbridge>" // (1L << 42)
case VARARGS => "<varargs>" // (1L << 43)
- case 0x100000000000L => "" // (1L << 44)
+ case TRIEDCOOKING => "<triedcooking>" // (1L << 44)
case 0x200000000000L => "" // (1L << 45)
case 0x400000000000L => "" // (1L << 46)
case 0x800000000000L => "" // (1L << 47)
case 0x1000000000000L => "" // (1L << 48)
- // case `latePRIVATE` => "<lateprivate>" // (1L << 49)
- // case `lateABSTRACT` => "<lateabstract>" // (1L << 50)
+ case 0x2000000000000L => "" // (1L << 49)
+ case 0x4000000000000L => "" // (1L << 50)
case `lateDEFERRED` => "<latedeferred>" // (1L << 51)
case `lateFINAL` => "<latefinal>" // (1L << 52)
case `lateMETHOD` => "<latemethod>" // (1L << 53)
@@ -398,10 +398,10 @@ class Flags extends ModifierFlags {
case `notPROTECTED` => "<notprotected>" // (1L << 56)
case `notOVERRIDE` => "<notoverride>" // (1L << 57)
case `notPRIVATE` => "<notprivate>" // (1L << 58)
- // case `notABSTRACT` => "<notabstract>" // (1L << 59)
- // case `notDEFERRED` => "<notdeferred>" // (1L << 60)
- // case `notFINAL` => "<notfinal>" // (1L << 61)
- // case `notMETHOD` => "<notmethod>" // (1L << 62)
+ case 0x800000000000000L => "" // (1L << 59)
+ case 0x1000000000000000L => "" // (1L << 60)
+ case 0x2000000000000000L => "" // (1L << 61)
+ case 0x4000000000000000L => "" // (1L << 62)
case 0x8000000000000000L => "" // (1L << 63)
case _ => ""
}
diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala
index aaf586b074..b36191b025 100644
--- a/src/compiler/scala/reflect/internal/Importers.scala
+++ b/src/compiler/scala/reflect/internal/Importers.scala
@@ -145,8 +145,8 @@ trait Importers { self: SymbolTable =>
PolyType(tparams map importSymbol, importType(restpe))
case from.NullaryMethodType(restpe) =>
NullaryMethodType(importType(restpe))
- case from.ConstantType(from.Constant(value)) =>
- ConstantType(Constant(value))
+ case from.ConstantType(constant @ from.Constant(_)) =>
+ ConstantType(importConstant(constant))
case from.SuperType(thistpe, supertpe) =>
SuperType(importType(thistpe), importType(supertpe))
case from.TypeBounds(lo, hi) =>
@@ -194,8 +194,8 @@ trait Importers { self: SymbolTable =>
})
def importAnnotArg(arg: from.ClassfileAnnotArg): ClassfileAnnotArg = arg match {
- case from.LiteralAnnotArg(from.Constant(value)) =>
- LiteralAnnotArg(Constant(value))
+ case from.LiteralAnnotArg(constant @ from.Constant(_)) =>
+ LiteralAnnotArg(importConstant(constant))
case from.ArrayAnnotArg(args) =>
ArrayAnnotArg(args map importAnnotArg)
case from.ScalaSigBytes(bytes) =>
@@ -303,8 +303,8 @@ trait Importers { self: SymbolTable =>
case _ =>
new Ident(importName(name))
}
- case from.Literal(from.Constant(value)) =>
- new Literal(Constant(value))
+ case from.Literal(constant @ from.Constant(_)) =>
+ new Literal(importConstant(constant))
case from.TypeTree() =>
new TypeTree()
case from.Annotated(annot, arg) =>
@@ -327,8 +327,19 @@ trait Importers { self: SymbolTable =>
null
}
if (mytree != null) {
- if (mytree hasSymbol) mytree.symbol = importSymbol(tree.symbol)
- mytree.tpe = importType(tree.tpe)
+ val mysym = if (tree hasSymbol) importSymbol(tree.symbol) else NoSymbol
+ val mytpe = importType(tree.tpe)
+
+ mytree match {
+ case mytt: TypeTree =>
+ val tt = tree.asInstanceOf[from.TypeTree]
+ if (mytree hasSymbol) mytt.symbol = mysym
+ if (tt.wasEmpty) mytt.defineType(mytpe) else mytt.setType(mytpe)
+ if (tt.original != null) mytt.setOriginal(importTree(tt.original))
+ case _ =>
+ if (mytree hasSymbol) mytree.symbol = importSymbol(tree.symbol)
+ mytree.tpe = importType(tree.tpe)
+ }
}
mytree
}
@@ -339,5 +350,10 @@ trait Importers { self: SymbolTable =>
def importRefTree(tree: from.RefTree): RefTree = importTree(tree).asInstanceOf[RefTree]
def importIdent(tree: from.Ident): Ident = importTree(tree).asInstanceOf[Ident]
def importCaseDef(tree: from.CaseDef): CaseDef = importTree(tree).asInstanceOf[CaseDef]
+ def importConstant(constant: from.Constant): Constant = new Constant(constant.tag match {
+ case ClassTag => importType(constant.value.asInstanceOf[from.Type])
+ case EnumTag => importSymbol(constant.value.asInstanceOf[from.Symbol])
+ case _ => constant.value
+ })
}
}
diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala
index b3069adfb4..b1a24c0be2 100644
--- a/src/compiler/scala/reflect/internal/StdNames.scala
+++ b/src/compiler/scala/reflect/internal/StdNames.scala
@@ -271,6 +271,7 @@ trait StdNames extends NameManglers { self: SymbolTable =>
// Compiler utilized names
// val productElementName: NameType = "productElementName"
val Ident: NameType = "Ident"
+ val This: NameType = "This"
val StringContext: NameType = "StringContext"
val TYPE_ : NameType = "TYPE"
val TypeTree: NameType = "TypeTree"
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala
index e504cea8cf..415b32958d 100644
--- a/src/compiler/scala/reflect/internal/Symbols.scala
+++ b/src/compiler/scala/reflect/internal/Symbols.scala
@@ -1274,6 +1274,16 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
* the annotations attached to member a definition (class, method, type, field).
*/
def annotations: List[AnnotationInfo] = _annotations
+
+ /** This getter is necessary for reflection, see https://issues.scala-lang.org/browse/SI-5423
+ * We could auto-inject completion into `annotations' and `setAnnotations', but I'm not sure about that
+ * @odersky writes: I fear we can't do the forcing for all compiler symbols as that could introduce cycles
+ */
+ def getAnnotations: List[AnnotationInfo] = {
+ initialize
+ _annotations
+ }
+
def setAnnotations(annots: List[AnnotationInfo]): this.type = {
_annotations = annots
this
diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala
index 3c339ad8e2..0004311647 100644
--- a/src/compiler/scala/reflect/internal/Trees.scala
+++ b/src/compiler/scala/reflect/internal/Trees.scala
@@ -266,8 +266,6 @@ trait Trees extends api.Trees { self: SymbolTable =>
def Super(sym: Symbol, mix: TypeName): Tree = Super(This(sym), mix)
- def This(sym: Symbol): Tree = This(sym.name.toTypeName) setSymbol sym
-
/** Block factory that flattens directly nested blocks.
*/
def Block(stats: Tree*): Block = {
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala
index 0d25ccf86d..3ed6544613 100644
--- a/src/compiler/scala/reflect/internal/Types.scala
+++ b/src/compiler/scala/reflect/internal/Types.scala
@@ -5708,7 +5708,7 @@ trait Types extends api.Types { self: SymbolTable =>
val padded = sorted map (_._2.padTo(maxSeqLength, NoType))
val transposed = padded.transpose
- val columns: List[Column[List[Type]]] = sorted.zipWithIndex map {
+ val columns: List[Column[List[Type]]] = mapWithIndex(sorted) {
case ((k, v), idx) =>
Column(str(k), (xs: List[Type]) => str(xs(idx)), true)
}
diff --git a/src/compiler/scala/reflect/runtime/JavaToScala.scala b/src/compiler/scala/reflect/runtime/JavaToScala.scala
index 813c66e1f0..89fd6bab64 100644
--- a/src/compiler/scala/reflect/runtime/JavaToScala.scala
+++ b/src/compiler/scala/reflect/runtime/JavaToScala.scala
@@ -241,16 +241,32 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable =>
* The Scala owner of the Scala class corresponding to the Java class `jclazz`
*/
private def sOwner(jclazz: jClass[_]): Symbol = {
- if (jclazz.isMemberClass)
- followStatic(classToScala(jclazz.getEnclosingClass), jclazz.getModifiers)
- else if (jclazz.isLocalClass)
- methodToScala(jclazz.getEnclosingMethod) orElse constrToScala(jclazz.getEnclosingConstructor)
- else if (jclazz.isPrimitive || jclazz.isArray)
+ if (jclazz.isMemberClass) {
+ val jEnclosingClass = jclazz.getEnclosingClass
+ val sEnclosingClass = classToScala(jEnclosingClass)
+ followStatic(sEnclosingClass, jclazz.getModifiers)
+ } else if (jclazz.isLocalClass) {
+ val jEnclosingMethod = jclazz.getEnclosingMethod
+ if (jEnclosingMethod != null) {
+ methodToScala(jEnclosingMethod)
+ } else {
+ val jEnclosingConstructor = jclazz.getEnclosingConstructor
+ constrToScala(jEnclosingConstructor)
+ }
+ } else if (jclazz.isPrimitive || jclazz.isArray) {
ScalaPackageClass
- else if (jclazz.getPackage != null)
- packageToScala(jclazz.getPackage)
- else
+ } else if (jclazz.getPackage != null) {
+ val jPackage = jclazz.getPackage
+ packageToScala(jPackage)
+ } else {
+ // @eb: a weird classloader might return a null package for something with a non-empty package name
+ // for example, http://groups.google.com/group/scala-internals/browse_thread/thread/7be09ff8f67a1e5c
+ // in that case we could invoke packageNameToScala(jPackageName) and, probably, be okay
+ // however, I think, it's better to blow up, since weirdness of the class loader might bite us elsewhere
+ val jPackageName = jclazz.getName.substring(0, Math.max(jclazz.getName.lastIndexOf("."), 0))
+ assert(jPackageName == "")
EmptyPackageClass
+ }
}
/**
@@ -295,8 +311,10 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable =>
* @return A Scala method object that corresponds to `jmeth`.
*/
def methodToScala(jmeth: jMethod): Symbol = methodCache.toScala(jmeth) {
- val owner = followStatic(classToScala(jmeth.getDeclaringClass), jmeth.getModifiers)
- lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth)
+ val jOwner = jmeth.getDeclaringClass
+ var sOwner = classToScala(jOwner)
+ sOwner = followStatic(sOwner, jmeth.getModifiers)
+ lookup(sOwner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth)
}
/**
@@ -344,6 +362,18 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable =>
pkg.moduleClass
}
+ private def scalaSimpleName(jclazz: jClass[_]): TypeName = {
+ val owner = sOwner(jclazz)
+ val enclosingClass = jclazz.getEnclosingClass
+ var prefix = if (enclosingClass != null) enclosingClass.getName else ""
+ val isObject = owner.isModuleClass && !owner.isPackageClass
+ if (isObject && !prefix.endsWith(nme.MODULE_SUFFIX_STRING)) prefix += nme.MODULE_SUFFIX_STRING
+ assert(jclazz.getName.startsWith(prefix))
+ var name = jclazz.getName.substring(prefix.length)
+ name = name.substring(name.lastIndexOf(".") + 1)
+ newTypeName(name)
+ }
+
/**
* The Scala class that corresponds to a given Java class.
* @param jclazz The Java class
@@ -353,28 +383,54 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable =>
*/
def classToScala(jclazz: jClass[_]): Symbol = classCache.toScala(jclazz) {
val jname = javaTypeName(jclazz)
- def lookup = sOwner(jclazz).info.decl(newTypeName(jclazz.getSimpleName))
-
- if (jclazz.isMemberClass && !nme.isImplClassName(jname)) {
- val sym = lookup
- assert(sym.isType, sym+"/"+jclazz+"/"+sOwner(jclazz)+"/"+jclazz.getSimpleName)
- sym.asInstanceOf[ClassSymbol]
- }
- else if (jclazz.isLocalClass || invalidClassName(jname)) {
- // local classes and implementation classes not preserved by unpickling - treat as Java
- jclassAsScala(jclazz)
- }
- else if (jclazz.isArray) {
- ArrayClass
+ val owner = sOwner(jclazz)
+ val simpleName = scalaSimpleName(jclazz)
+
+ val sym = {
+ def lookup = {
+ def coreLookup(name: Name): Symbol = {
+ val sym = owner.info.decl(name)
+ sym orElse {
+ if (name.startsWith(nme.NAME_JOIN_STRING))
+ coreLookup(name.subName(1, name.length))
+ else
+ NoSymbol
+ }
+ }
+
+ if (nme.isModuleName(simpleName)) {
+ val moduleName = nme.stripModuleSuffix(simpleName).toTermName
+ val sym = coreLookup(moduleName)
+ if (sym == NoSymbol) sym else sym.moduleClass
+ } else {
+ coreLookup(simpleName)
+ }
+ }
+
+ if (jclazz.isMemberClass && !nme.isImplClassName(jname)) {
+ lookup
+ } else if (jclazz.isLocalClass || invalidClassName(jname)) {
+ // local classes and implementation classes not preserved by unpickling - treat as Java
+ jclassAsScala(jclazz)
+ } else if (jclazz.isArray) {
+ ArrayClass
+ } else javaTypeToValueClass(jclazz) orElse {
+ // jclazz is top-level - get signature
+ lookup
+ // val (clazz, module) = createClassModule(
+ // sOwner(jclazz), newTypeName(jclazz.getSimpleName), new TopClassCompleter(_, _))
+ // classCache enter (jclazz, clazz)
+ // clazz
+ }
}
- else javaTypeToValueClass(jclazz) orElse {
- // jclazz is top-level - get signature
- lookup
- // val (clazz, module) = createClassModule(
- // sOwner(jclazz), newTypeName(jclazz.getSimpleName), new TopClassCompleter(_, _))
- // classCache enter (jclazz, clazz)
- // clazz
+
+ if (!sym.isType) {
+ def msgNoSym = "no symbol could be loaded from %s (scala equivalent is %s) by name %s".format(owner, jclazz, simpleName)
+ def msgIsNotType = "not a type: symbol %s loaded from %s (scala equivalent is %s) by name %s".format(sym, owner, jclazz, simpleName)
+ assert(false, if (sym == NoSymbol) msgNoSym else msgIsNotType)
}
+
+ sym.asInstanceOf[ClassSymbol]
}
/**
@@ -453,7 +509,7 @@ trait JavaToScala extends ConversionUtil { self: SymbolTable =>
private def jclassAsScala(jclazz: jClass[_]): Symbol = jclassAsScala(jclazz, sOwner(jclazz))
private def jclassAsScala(jclazz: jClass[_], owner: Symbol): Symbol = {
- val name = newTypeName(jclazz.getSimpleName)
+ val name = scalaSimpleName(jclazz)
val completer = (clazz: Symbol, module: Symbol) => new FromJavaClassCompleter(clazz, module, jclazz)
val (clazz, module) = createClassModule(owner, name, completer)
classCache enter (jclazz, clazz)
diff --git a/src/compiler/scala/reflect/runtime/ToolBoxes.scala b/src/compiler/scala/reflect/runtime/ToolBoxes.scala
index 46d890c5d1..6e671ae06e 100644
--- a/src/compiler/scala/reflect/runtime/ToolBoxes.scala
+++ b/src/compiler/scala/reflect/runtime/ToolBoxes.scala
@@ -44,17 +44,19 @@ trait ToolBoxes extends { self: Universe =>
// !!! Why is this is in the empty package? If it's only to make
// it inaccessible then please put it somewhere designed for that
// rather than polluting the empty package with synthetics.
+ trace("typing: ")(showAttributed(tree))
val ownerClass = EmptyPackageClass.newClassWithInfo(newTypeName("<expression-owner>"), List(ObjectClass.tpe), newScope)
val owner = ownerClass.newLocalDummy(tree.pos)
-
- typer.atOwner(tree, owner).typed(tree, analyzer.EXPRmode, pt)
+ val ttree = typer.atOwner(tree, owner).typed(tree, analyzer.EXPRmode, pt)
+ trace("typed: ")(showAttributed(ttree))
+ ttree
}
-
+
def defOwner(tree: Tree): Symbol = tree find (_.isDef) map (_.symbol) match {
case Some(sym) if sym != null && sym != NoSymbol => sym.owner
case _ => NoSymbol
}
-
+
def wrapInObject(expr: Tree, fvs: List[Symbol]): ModuleDef = {
val obj = EmptyPackageClass.newModule(nextWrapperModuleName())
val minfo = ClassInfoType(List(ObjectClass.tpe, ScalaObjectClass.tpe), newScope, obj.moduleClass)
@@ -66,9 +68,7 @@ trait ToolBoxes extends { self: Universe =>
minfo.decls enter meth
trace("wrapping ")(defOwner(expr) -> meth)
val methdef = DefDef(meth, expr changeOwner (defOwner(expr) -> meth))
- trace("wrapped: ")(showAttributed(methdef))
- resetAllAttrs(
- ModuleDef(
+ val moduledef = ModuleDef(
obj,
Template(
List(TypeTree(ObjectClass.tpe)),
@@ -77,7 +77,11 @@ trait ToolBoxes extends { self: Universe =>
List(),
List(List()),
List(methdef),
- NoPosition)))
+ NoPosition))
+ trace("wrapped: ")(showAttributed(moduledef))
+ val cleanedUp = resetLocalAttrs(moduledef)
+ trace("cleaned up: ")(showAttributed(cleanedUp))
+ cleanedUp
}
def wrapInPackage(clazz: Tree): PackageDef =
@@ -91,7 +95,7 @@ trait ToolBoxes extends { self: Universe =>
def compileExpr(expr: Tree, fvs: List[Symbol]): String = {
val mdef = wrapInObject(expr, fvs)
- val pdef = trace("wrapped: ")(wrapInPackage(mdef))
+ val pdef = wrapInPackage(mdef)
val unit = wrapInCompilationUnit(pdef)
val run = new Run
run.compileUnits(List(unit), run.namerPhase)
@@ -104,24 +108,27 @@ trait ToolBoxes extends { self: Universe =>
def runExpr(expr: Tree): Any = {
val etpe = expr.tpe
val fvs = (expr filter isFree map (_.symbol)).distinct
-
+
reporter.reset()
val className = compileExpr(expr, fvs)
if (reporter.hasErrors) {
throw new Error("reflective compilation has failed")
}
-
+
if (settings.debug.value) println("generated: "+className)
val jclazz = jClass.forName(moduleFileName(className), true, classLoader)
val jmeth = jclazz.getDeclaredMethods.find(_.getName == wrapperMethodName).get
val jfield = jclazz.getDeclaredFields.find(_.getName == NameTransformer.MODULE_INSTANCE_NAME).get
val singleton = jfield.get(null)
- val result = jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*)
- if (etpe.typeSymbol != FunctionClass(0)) result
- else {
- val applyMeth = result.getClass.getMethod("apply")
- applyMeth.invoke(result)
- }
+ // @odersky writes: Not sure we will be able to drop this. I forgot the reason why we dereference () functions,
+ // but there must have been one. So I propose to leave old version in comments to be resurrected if the problem resurfaces.
+// val result = jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*)
+// if (etpe.typeSymbol != FunctionClass(0)) result
+// else {
+// val applyMeth = result.getClass.getMethod("apply")
+// applyMeth.invoke(result)
+// }
+ jmeth.invoke(singleton, fvs map (sym => sym.asInstanceOf[FreeVar].value.asInstanceOf[AnyRef]): _*)
}
def showAttributed(tree: Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = {
@@ -131,7 +138,7 @@ trait ToolBoxes extends { self: Universe =>
try {
settings.printtypes.value = printTypes
settings.uniqid.value = printIds
- settings.uniqid.value = printKinds
+ settings.Yshowsymkinds.value = printKinds
tree.toString
} finally {
settings.printtypes.value = saved1
@@ -167,7 +174,7 @@ trait ToolBoxes extends { self: Universe =>
lazy val exporter = importer.reverse
lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, defaultReflectiveClassLoader)
-
+
private def importAndTypeCheck(tree: rm.Tree, expectedType: rm.Type): compiler.Tree = {
// need to establish a run an phase because otherwise we run into an assertion in TypeHistory
// that states that the period must be different from NoPeriod
@@ -189,8 +196,8 @@ trait ToolBoxes extends { self: Universe =>
def typeCheck(tree: rm.Tree): rm.Tree =
typeCheck(tree, WildcardType.asInstanceOf[rm.Type])
- def showAttributed(tree: rm.Tree): String =
- compiler.showAttributed(importer.importTree(tree.asInstanceOf[Tree]))
+ def showAttributed(tree: rm.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String =
+ compiler.showAttributed(importer.importTree(tree.asInstanceOf[Tree]), printTypes, printIds, printKinds)
def runExpr(tree: rm.Tree, expectedType: rm.Type): Any = {
val ttree = importAndTypeCheck(tree, expectedType)
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index c80b07c44d..83b6252b26 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -42,8 +42,8 @@ trait Trees extends reflect.internal.Trees { self: Global =>
/** emitted by typer, eliminated by refchecks */
case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree
-
- /** Marks underlying reference to id as boxed.
+
+ /** Marks underlying reference to id as boxed.
* @pre: id must refer to a captured variable
* A reference such marked will refer to the boxed entity, no dereferencing
* with `.elem` is done on it.
@@ -208,7 +208,7 @@ trait Trees extends reflect.internal.Trees { self: Global =>
case _ => this.treeCopy.SelectFromArray(tree, qualifier, selector, erasure)
}
def ReferenceToBoxed(tree: Tree, idt: Ident) = tree match {
- case t @ ReferenceToBoxed(idt0)
+ case t @ ReferenceToBoxed(idt0)
if (idt0 == idt) => t
case _ => this.treeCopy.ReferenceToBoxed(tree, idt)
}
@@ -251,62 +251,79 @@ trait Trees extends reflect.internal.Trees { self: Global =>
}
}
- /** resets symbol and tpe fields in a tree, @see ResetAttrsTraverse
+ /** resets symbol and tpe fields in a tree, @see ResetAttrs
*/
// def resetAllAttrs[A<:Tree](x:A): A = { new ResetAttrsTraverser().traverse(x); x }
// def resetLocalAttrs[A<:Tree](x:A): A = { new ResetLocalAttrsTraverser().traverse(x); x }
-
- def resetAllAttrs[A<:Tree](x:A): A = new ResetAttrsTransformer(false).transformPoly(x)
- def resetLocalAttrs[A<:Tree](x:A): A = new ResetAttrsTransformer(true).transformPoly(x)
+
+ def resetAllAttrs[A<:Tree](x:A): A = new ResetAttrs(false).transform(x)
+ def resetLocalAttrs[A<:Tree](x:A): A = new ResetAttrs(true).transform(x)
/** A transformer which resets symbol and tpe fields of all nodes in a given tree,
* with special treatment of:
* TypeTree nodes: are replaced by their original if it exists, otherwise tpe field is reset
* to empty if it started out empty or refers to local symbols (which are erased).
* TypeApply nodes: are deleted if type arguments end up reverted to empty
- * This(pkg) notes where pkg is a pckage: these are kept.
+ * This(pkg) nodes where pkg is a package: these are kept.
*
- * (bq:) This traverser has mutable state and should be discarded after use
+ * (bq:) This transformer has mutable state and should be discarded after use
*/
- private class ResetAttrsTransformer(localOnly: Boolean) extends Transformer {
- private val erasedSyms = util.HashSet[Symbol](8)
- private def resetDef(tree: Tree) {
- if (tree.symbol != null && tree.symbol != NoSymbol)
- erasedSyms addEntry tree.symbol
- tree.symbol = NoSymbol
+ private class ResetAttrs(localOnly: Boolean) {
+ val locals = util.HashSet[Symbol](8)
+
+ class MarkLocals extends self.Traverser {
+ def markLocal(tree: Tree) =
+ if (tree.symbol != null && tree.symbol != NoSymbol)
+ locals addEntry tree.symbol
+
+ override def traverse(tree: Tree) = {
+ tree match {
+ case _: DefTree | Function(_, _) | Template(_, _, _) =>
+ markLocal(tree)
+ case _ if tree.symbol.isInstanceOf[FreeVar] =>
+ markLocal(tree)
+ case _ =>
+ ;
+ }
+
+ super.traverse(tree)
+ }
}
- override def transform(tree: Tree): Tree = super.transform {
- tree match {
- case Template(_, _, body) =>
- body foreach resetDef
- resetDef(tree)
- tree.tpe = null
- tree
- case _: DefTree | Function(_, _) | Template(_, _, _) =>
- resetDef(tree)
- tree.tpe = null
- tree
- case tpt: TypeTree =>
- if (tpt.original != null)
- tpt.original
- else if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => erasedSyms contains tp.typeSymbol))))
- tpt.tpe = null
- tree
- case TypeApply(fn, args) if args map transform exists (_.isEmpty) =>
- fn
- case This(_) if tree.symbol != null && tree.symbol.isPackageClass =>
- tree
- case EmptyTree =>
- tree
- case _ =>
- if (tree.hasSymbol && (!localOnly || (erasedSyms contains tree.symbol)))
- tree.symbol = NoSymbol
- tree.tpe = null
- tree
+
+ class Transformer extends self.Transformer {
+ override def transform(tree: Tree): Tree = super.transform {
+ tree match {
+ case tpt: TypeTree =>
+ if (tpt.original != null) {
+ transform(tpt.original)
+ } else {
+ if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => locals contains tp.typeSymbol))))
+ tpt.tpe = null
+ tree
+ }
+ case TypeApply(fn, args) if args map transform exists (_.isEmpty) =>
+ transform(fn)
+ case This(_) if tree.symbol != null && tree.symbol.isPackageClass =>
+ tree
+ case EmptyTree =>
+ tree
+ case _ =>
+ if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)))
+ tree.symbol = NoSymbol
+ tree.tpe = null
+ tree
+ }
}
}
- def transformPoly[T <: Tree](x: T): T = {
- val x1 = transform(x)
+
+ def transform[T <: Tree](x: T): T = {
+ new MarkLocals().traverse(x)
+
+ val trace = scala.tools.nsc.util.trace when settings.debug.value
+ val eoln = System.getProperty("line.separator")
+ trace("locals (%d total): %n".format(locals.size))(locals.toList map {" " + _} mkString eoln)
+
+ val x1 = new Transformer().transform(x)
assert(x.getClass isInstance x1)
x1.asInstanceOf[T]
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 4478fb6128..dae264fffe 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -84,6 +84,8 @@ trait Scanners extends ScannersCommon {
abstract class Scanner extends CharArrayReader with TokenData with ScannerCommon {
private def isDigit(c: Char) = java.lang.Character isDigit c
+
+ def isAtEnd = charOffset >= buf.length
def flush = { charOffset = offset; nextChar(); this }
@@ -449,7 +451,7 @@ trait Scanners extends ScannersCommon {
case ']' =>
nextChar(); token = RBRACKET
case SU =>
- if (charOffset >= buf.length) token = EOF
+ if (isAtEnd) token = EOF
else {
syntaxError("illegal character")
nextChar()
@@ -771,10 +773,10 @@ trait Scanners extends ScannersCommon {
putChar(ch)
}
- private def getLitChars(delimiter: Char) =
- while (ch != delimiter && (ch != CR && ch != LF && ch != SU || isUnicodeEscape)) {
+ private def getLitChars(delimiter: Char) = {
+ while (ch != delimiter && !isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape))
getLitChar()
- }
+ }
/** read fractional part and exponent of floating point number
* if one is present.
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 3d650ef753..3baff7da9e 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -1072,6 +1072,15 @@ abstract class GenICode extends SubComponent {
targets = tmpCtx.bb :: targets
case Ident(nme.WILDCARD) =>
default = tmpCtx.bb
+ case Alternative(alts) =>
+ alts foreach {
+ case Literal(value) =>
+ tags = value.intValue :: tags
+ targets = tmpCtx.bb :: targets
+ case _ =>
+ abort("Invalid case in alternative in switch-like pattern match: " +
+ tree + " at: " + tree.pos)
+ }
case _ =>
abort("Invalid case statement in switch-like pattern match: " +
tree + " at: " + (tree.pos))
diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
index ea36eb03c7..efa524503c 100644
--- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala
@@ -196,11 +196,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
/** The start of a scaladoc code block */
protected val CodeBlockStart =
- new Regex("""(.*)((?:\{\{\{)|(?:\u000E<pre(?: [^>]*)?>\u000E))(.*)""")
+ new Regex("""(.*?)((?:\{\{\{)|(?:\u000E<pre(?: [^>]*)?>\u000E))(.*)""")
/** The end of a scaladoc code block */
protected val CodeBlockEnd =
- new Regex("""(.*)((?:\}\}\})|(?:\u000E</pre>\u000E))(.*)""")
+ new Regex("""(.*?)((?:\}\}\})|(?:\u000E</pre>\u000E))(.*)""")
/** A key used for a tag map. The key is built from the name of the tag and
* from the linked symbol if the tag has one.
@@ -250,7 +250,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* @param remaining The lines that must still recursively be parsed.
* @param inCodeBlock Whether the next line is part of a code block (in which no tags must be read). */
def parse0 (
- docBody: String,
+ docBody: StringBuilder,
tags: Map[TagKey, List[String]],
lastTagKey: Option[TagKey],
remaining: List[String],
@@ -258,9 +258,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
): Comment = remaining match {
case CodeBlockStart(before, marker, after) :: ls if (!inCodeBlock) =>
- if (before.trim != "")
- parse0(docBody, tags, lastTagKey, before :: (marker + after) :: ls, false)
- else if (after.trim != "")
+ if (!before.trim.isEmpty && !after.trim.isEmpty)
+ parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, false)
+ else if (!before.trim.isEmpty)
+ parse0(docBody, tags, lastTagKey, before :: marker :: ls, false)
+ else if (!after.trim.isEmpty)
parse0(docBody, tags, lastTagKey, marker :: after :: ls, true)
else lastTagKey match {
case Some(key) =>
@@ -271,24 +273,26 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
parse0(docBody, tags + (key -> value), lastTagKey, ls, true)
case None =>
- parse0(docBody + endOfLine + marker, tags, lastTagKey, ls, true)
+ parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, true)
}
case CodeBlockEnd(before, marker, after) :: ls =>
- if (before.trim != "")
- parse0(docBody, tags, lastTagKey, before :: (marker + after) :: ls, true)
- else if (after.trim != "")
+ if (!before.trim.isEmpty && !after.trim.isEmpty)
+ parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, true)
+ if (!before.trim.isEmpty)
+ parse0(docBody, tags, lastTagKey, before :: marker :: ls, true)
+ else if (!after.trim.isEmpty)
parse0(docBody, tags, lastTagKey, marker :: after :: ls, false)
else lastTagKey match {
case Some(key) =>
val value =
((tags get key): @unchecked) match {
- case Some(b :: bs) => (b + endOfLine + "}}}") :: bs
+ case Some(b :: bs) => (b + endOfLine + marker) :: bs
case None => oops("lastTagKey set when no tag exists for key")
}
parse0(docBody, tags + (key -> value), lastTagKey, ls, false)
case None =>
- parse0(docBody + endOfLine + marker, tags, lastTagKey, ls, false)
+ parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, false)
}
case SymbolTag(name, sym, body) :: ls if (!inCodeBlock) =>
@@ -311,8 +315,9 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
parse0(docBody, tags + (key -> value), lastTagKey, ls, inCodeBlock)
case line :: ls =>
- val newBody = if (docBody == "") line else docBody + endOfLine + line
- parse0(newBody, tags, lastTagKey, ls, inCodeBlock)
+ if (docBody.length > 0) docBody append endOfLine
+ docBody append line
+ parse0(docBody, tags, lastTagKey, ls, inCodeBlock)
case Nil =>
@@ -350,7 +355,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
val com = createComment (
- body0 = Some(parseWiki(docBody, pos)),
+ body0 = Some(parseWiki(docBody.toString, pos)),
authors0 = allTags(SimpleTagKey("author")),
see0 = allTags(SimpleTagKey("see")),
result0 = oneTag(SimpleTagKey("return")),
@@ -374,7 +379,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
- parse0("", Map.empty, None, clean(comment), false)
+ parse0(new StringBuilder(comment.size), Map.empty, None, clean(comment), false)
}
@@ -385,7 +390,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* - Removed all end-of-line whitespace.
* - Only `endOfLine` is used to mark line endings. */
def parseWiki(string: String, pos: Position): Body = {
- new WikiParser(string.toArray, pos).document()
+ new WikiParser(string, pos).document()
}
/** TODO
@@ -393,7 +398,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
* @author Ingo Maier
* @author Manohar Jonnalagedda
* @author Gilles Dubochet */
- protected final class WikiParser(val buffer: Array[Char], pos: Position) extends CharReader(buffer) { wiki =>
+ protected final class WikiParser(val buffer: String, pos: Position) extends CharReader(buffer) { wiki =>
var summaryParsed = false
@@ -411,7 +416,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
def block(): Block = {
if (checkSkipInitWhitespace("{{{"))
code()
- else if (checkSkipInitWhitespace("="))
+ else if (checkSkipInitWhitespace('='))
title()
else if (checkSkipInitWhitespace("----"))
hrule()
@@ -493,7 +498,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
def title(): Block = {
jumpWhitespace()
val inLevel = repeatJump("=")
- val text = inline(check(Array.fill(inLevel)('=')))
+ val text = inline(check("=" * inLevel))
val outLevel = repeatJump("=", inLevel)
if (inLevel != outLevel)
reportError(pos, "unbalanced or unclosed heading")
@@ -734,11 +739,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
val pc = char
nextChar() // read EOL
val ok = {
- checkSkipInitWhitespace(Array(endOfLine)) ||
- checkSkipInitWhitespace(Array('=')) ||
- checkSkipInitWhitespace(Array('{', '{', '{')) ||
+ checkSkipInitWhitespace(endOfLine) ||
+ checkSkipInitWhitespace('=') ||
+ checkSkipInitWhitespace("{{{") ||
checkList ||
- checkSkipInitWhitespace(Array('\u003D'))
+ checkSkipInitWhitespace('\u003D')
}
offset = poff
char = pc
@@ -751,7 +756,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
}
}
- protected sealed class CharReader(buffer: Array[Char]) { reader =>
+ protected sealed class CharReader(buffer: String) { reader =>
var char: Char = _
var offset: Int = 0
@@ -760,36 +765,37 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
if (offset >= buffer.length)
char = endOfText
else {
- char = buffer(offset)
+ char = buffer charAt offset
offset += 1
}
}
- implicit def strintToChars(s: String): Array[Char] = s.toArray
-
- def store(body: => Unit): String = {
- val pre = offset
- body
- val post = offset
- buffer.toArray.slice(pre, post).toString
+ final def check(chars: String): Boolean = {
+ val poff = offset
+ val pc = char
+ val ok = jump(chars)
+ offset = poff
+ char = pc
+ ok
}
- final def check(chars: Array[Char]): Boolean = {
+ def checkSkipInitWhitespace(c: Char): Boolean = {
val poff = offset
val pc = char
- val ok = jump(chars)
+ jumpWhitespace()
+ val ok = jump(c)
offset = poff
char = pc
ok
}
- def checkSkipInitWhitespace(chars: Array[Char]): Boolean = {
+ def checkSkipInitWhitespace(chars: String): Boolean = {
val poff = offset
val pc = char
jumpWhitespace()
val (ok0, chars0) =
- if (chars.head == ' ')
- (offset > poff, chars.tail)
+ if (chars.charAt(0) == ' ')
+ (offset > poff, chars substring 1)
else
(true, chars)
val ok = ok0 && jump(chars0)
@@ -825,16 +831,16 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
/** jumps all the characters in chars, consuming them in the process.
* @return true only if the correct characters have been jumped */
- final def jump(chars: Array[Char]): Boolean = {
+ final def jump(chars: String): Boolean = {
var index = 0
- while (index < chars.length && char == chars(index) && char != endOfText) {
+ while (index < chars.length && char == chars.charAt(index) && char != endOfText) {
nextChar()
index += 1
}
index == chars.length
}
- final def checkedJump(chars: Array[Char]): Boolean = {
+ final def checkedJump(chars: String): Boolean = {
val poff = offset
val pc = char
val ok = jump(chars)
@@ -845,7 +851,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
ok
}
- final def repeatJump(chars: Array[Char], max: Int): Int = {
+ final def repeatJump(chars: String, max: Int): Int = {
var count = 0
var more = true
while (more && count < max) {
@@ -857,7 +863,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
count
}
- final def repeatJump(chars: Array[Char]): Int = {
+ final def repeatJump(chars: String): Int = {
var count = 0
var more = true
while (more) {
@@ -878,10 +884,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
count
}
- final def jumpUntil(chars: Array[Char]): Int = {
+ final def jumpUntil(chars: String): Int = {
assert(chars.length > 0)
var count = 0
- val c = chars(0)
+ val c = chars.charAt(0)
while (!check(chars) && char != endOfText) {
nextChar()
while (char != c && char != endOfText) {
@@ -922,10 +928,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory =>
count
}
- final def readUntil(chars: Array[Char]): Int = {
+ final def readUntil(chars: String): Int = {
assert(chars.length > 0)
var count = 0
- val c = chars(0)
+ val c = chars.charAt(0)
while (!check(chars) && char != endOfText) {
readBuilder += char
nextChar()
diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
index 3bc4e1cbe1..3a605975f4 100644
--- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
@@ -20,6 +20,7 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader)
with ScalaClassLoader
{
// private val defined = mutable.Map[String, Class[_]]()
+
override protected def trace =
sys.props contains "scala.debug.classloader"
@@ -43,6 +44,22 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader)
}
}
+ protected def dirNameToPath(name: String): String =
+ name.replace('.', '/')
+
+ protected def findAbstractDir(name: String): AbstractFile = {
+ var file: AbstractFile = root
+ val pathParts = dirNameToPath(name) split '/'
+
+ for (dirPart <- pathParts) {
+ file = file.lookupName(dirPart, true)
+ if (file == null)
+ return null
+ }
+
+ return file
+ }
+
override def getResourceAsStream(name: String) = findAbstractFile(name) match {
case null => super.getResourceAsStream(name)
case file => file.input
@@ -74,4 +91,24 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader)
// case null => super.getResource(name)
// case file => new URL(...)
// }
+
+ private val packages = mutable.Map[String, Package]()
+
+ override def definePackage(name: String, specTitle: String, specVersion: String, specVendor: String, implTitle: String, implVersion: String, implVendor: String, sealBase: URL): Package = {
+ throw new UnsupportedOperationException()
+ }
+
+ override def getPackage(name: String): Package = {
+ findAbstractDir(name) match {
+ case null => super.getPackage(name)
+ case file => packages.getOrElseUpdate(name, {
+ val ctor = classOf[Package].getDeclaredConstructor(classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[URL], classOf[ClassLoader])
+ ctor.setAccessible(true)
+ ctor.newInstance(name, null, null, null, null, null, null, null, this)
+ })
+ }
+ }
+
+ override def getPackages(): Array[Package] =
+ root.iterator.filter(_.isDirectory).map(dir => getPackage(dir.name)).toArray
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 567d6c2f78..6ae8d0e7d0 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -196,7 +196,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
def foreach[U](f: Tree => U): Unit = t foreach { x => f(x) ; () }
}).toList
}
-
+
implicit def installReplTypeOps(tp: Type): ReplTypeOps = new ReplTypeOps(tp)
class ReplTypeOps(tp: Type) {
def orElse(other: => Type): Type = if (tp ne NoType) tp else other
@@ -831,7 +831,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
case xs => sys.error("Internal error: eval object " + evalClass + ", " + xs.mkString("\n", "\n", ""))
}
private def compileAndSaveRun(label: String, code: String) = {
- showCodeIfDebugging(code)
+ showCodeIfDebugging(packaged(code))
val (success, run) = compileSourcesKeepingRun(new BatchSourceFile(label, packaged(code)))
lastRun = run
success
@@ -1078,7 +1078,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
val clazz = classOfTerm(id) getOrElse { return NoType }
val staticSym = tpe.typeSymbol
val runtimeSym = getClassIfDefined(clazz.getName)
-
+
if ((runtimeSym != NoSymbol) && (runtimeSym != staticSym) && (runtimeSym isSubClass staticSym))
runtimeSym.info
else NoType
diff --git a/src/compiler/scala/tools/nsc/io/Path.scala b/src/compiler/scala/tools/nsc/io/Path.scala
index 9efff089ba..a1b8e5e4d5 100644
--- a/src/compiler/scala/tools/nsc/io/Path.scala
+++ b/src/compiler/scala/tools/nsc/io/Path.scala
@@ -48,7 +48,7 @@ object Path {
implicit def jfile2path(jfile: JFile): Path = apply(jfile)
// java 7 style, we don't use it yet
- // object AccessMode extends Enumeration("AccessMode") {
+ // object AccessMode extends Enumeration {
// val EXECUTE, READ, WRITE = Value
// }
// def checkAccess(modes: AccessMode*): Boolean = {
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 107ffc35c6..d1ce460eb9 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -166,7 +166,9 @@ trait ScalaSettings extends AbsScalaSettings
val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.")
val Yreifycopypaste =
BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
- val Yreifydebug = BooleanSetting ("-Yreify-debug", "Trace reification actions.")
+ val Yreifydebug = BooleanSetting ("-Yreify-debug", "Trace reification.")
+ val Yreifytyperdebug
+ = BooleanSetting ("-Yreifytyper-debug", "Trace typings of reified trees.")
val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.") .
withPostSetHook(_ => interpreter.replProps.debug setValue true)
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index a0826c19c3..d8db62a408 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -238,9 +238,9 @@ abstract class ClassfileParser {
val index = in.getChar(start + 1)
val name = getExternalName(in.getChar(starts(index) + 1))
//assert(name.endsWith("$"), "Not a module class: " + name)
- f = forceMangledName(name.subName(0, name.length - 1), true)
+ f = forceMangledName(name dropRight 1, true)
if (f == NoSymbol)
- f = definitions.getModule(name.subName(0, name.length - 1))
+ f = definitions.getModule(name dropRight 1)
} else {
val origName = nme.originalName(name)
val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol
@@ -1074,27 +1074,27 @@ abstract class ClassfileParser {
}
def enterClassAndModule(entry: InnerClassEntry, completer: global.loaders.SymbolLoader, jflags: Int) {
- val name = entry.originalName
- var sflags = toScalaClassFlags(jflags)
+ val name = entry.originalName
+ var sflags = toScalaClassFlags(jflags)
+ val owner = getOwner(jflags)
+ val scope = getScope(jflags)
+ val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer
+ val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer
- val innerClass = getOwner(jflags).newClass(name.toTypeName).setInfo(completer).setFlag(sflags)
- val innerModule = getOwner(jflags).newModule(name.toTermName).setInfo(completer).setFlag(sflags)
innerModule.moduleClass setInfo global.loaders.moduleClassLoader
-
- getScope(jflags) enter innerClass
- getScope(jflags) enter innerModule
+ scope enter innerClass
+ scope enter innerModule
val decls = innerClass.enclosingPackage.info.decls
- val e = decls.lookupEntry(className(entry.externalName))
- if (e ne null) {
- //println("removing " + e)
- decls.unlink(e)
- }
- val e1 = decls.lookupEntry(className(entry.externalName).toTypeName)
- if (e1 ne null) {
- //println("removing " + e1)
- decls.unlink(e1)
+ def unlinkIfPresent(name: Name) = {
+ val e = decls lookupEntry name
+ if (e ne null)
+ decls unlink e
}
+
+ val cName = className(entry.externalName)
+ unlinkIfPresent(cName.toTermName)
+ unlinkIfPresent(cName.toTypeName)
}
for (entry <- innerClasses.values) {
diff --git a/src/compiler/scala/tools/nsc/transform/LiftCode.scala b/src/compiler/scala/tools/nsc/transform/LiftCode.scala
index 26568b4edc..8affea56a2 100644
--- a/src/compiler/scala/tools/nsc/transform/LiftCode.scala
+++ b/src/compiler/scala/tools/nsc/transform/LiftCode.scala
@@ -56,10 +56,16 @@ abstract class LiftCode extends Transform with TypingTransformers {
class Codifier(unit: CompilationUnit) extends TypingTransformer(unit) {
val reifyDebug = settings.Yreifydebug.value
+ val reifyTyperDebug = settings.Yreifytyperdebug.value
val debugTrace = util.trace when reifyDebug
val reifyCopypaste = settings.Yreifycopypaste.value
def printCopypaste(tree: Tree) {
+ if (reifyDebug) println("=======================")
+ printCopypaste1(tree)
+ if (reifyDebug) println("=======================")
+ }
+ def printCopypaste1(tree: Tree) {
import scala.reflect.api.Modifier
import scala.reflect.api.Modifier._
@@ -124,13 +130,22 @@ abstract class LiftCode extends Transform with TypingTransformers {
case Apply(_, List(tree)) if sym == Code_lift => // reify Code.lift[T](expr) instances
val saved = printTypings
try {
- printTypings = reifyDebug
+ debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
debugTrace("transformed = ") {
- val result = localTyper.typedPos(tree.pos)(codify(super.transform(tree)))
- if (reifyCopypaste) printCopypaste(result)
- result
+ val untyped = codify(super.transform(tree))
+ if (reifyCopypaste) printCopypaste(untyped)
+
+ printTypings = reifyTyperDebug
+ val typed = localTyper.typedPos(tree.pos)(untyped)
+ typed
}
- } finally printTypings = saved
+ } catch {
+ case ex: ReifierError =>
+ unit.error(ex.pos, ex.msg)
+ tree
+ } finally {
+ printTypings = saved
+ }
case _ =>
super.transform(tree)
}
@@ -140,7 +155,8 @@ abstract class LiftCode extends Transform with TypingTransformers {
val targetType = definitions.CodeClass.primaryConstructor.info.paramTypes.head
val reifier = new Reifier()
val arg = gen.mkAsInstanceOf(reifier.reifyTopLevel(tree), targetType, wrapInApply = false)
- val treetpe =
+ val treetpe = // this really should use packedType(tree.tpe, context.owner)
+ // where packedType is defined in Typers. But we can do that only if liftCode is moved to Typers.
if (tree.tpe.typeSymbol.isAnonymousClass) tree.tpe.typeSymbol.classBound
else tree.tpe
New(TypeTree(appliedType(definitions.CodeClass.typeConstructor, List(treetpe.widen))),
@@ -269,6 +285,14 @@ abstract class LiftCode extends Transform with TypingTransformers {
case None =>
if (sym == NoSymbol)
mirrorSelect("NoSymbol")
+ else if (sym == RootPackage)
+ mirrorSelect("definitions.RootPackage")
+ else if (sym == RootClass)
+ mirrorSelect("definitions.RootClass")
+ else if (sym == EmptyPackage)
+ mirrorSelect("definitions.EmptyPackage")
+ else if (sym == EmptyPackageClass)
+ mirrorSelect("definitions.EmptyPackageClass")
else if (sym.isModuleClass)
Select(reifySymRef(sym.sourceModule), "moduleClass")
else if (sym.isStatic && sym.isClass)
@@ -295,7 +319,7 @@ abstract class LiftCode extends Transform with TypingTransformers {
if (sym.isTerm) {
if (reifyDebug) println("Free: " + sym)
val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false)
- def markIfCaptured(arg: Ident): Tree =
+ def markIfCaptured(arg: Ident): Tree =
if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg
mirrorCall("freeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym)))
} else {
@@ -376,6 +400,14 @@ abstract class LiftCode extends Transform with TypingTransformers {
}
}
+ private def definedInLiftedCode(tpe: Type) =
+ tpe exists (tp => boundSyms contains tp.typeSymbol)
+
+ private def isErased(tree: Tree) = tree match {
+ case tt: TypeTree => definedInLiftedCode(tt.tpe) && tt.original == null
+ case _ => false
+ }
+
/** Reify a tree */
private def reifyTree(tree: Tree): Tree = tree match {
case EmptyTree =>
@@ -388,18 +420,32 @@ abstract class LiftCode extends Transform with TypingTransformers {
mirrorCall("Select", reifyFree(tree), reifyName(nme.elem))
} else reifyFree(tree)
case tt: TypeTree if (tt.tpe != null) =>
- if (!(boundSyms exists (tt.tpe contains _))) mirrorCall("TypeTree", reifyType(tt.tpe))
- else if (tt.original != null) reify(tt.original)
- else mirrorCall(nme.TypeTree)
+ if (definedInLiftedCode(tt.tpe)) {
+ // erase non-essential (i.e. inferred) types
+ // reify symless counterparts of essential types
+ if (tt.original != null) reify(tt.original) else mirrorCall("TypeTree")
+ } else {
+ var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe))
+ if (tt.original != null) {
+ val setOriginal = Select(rtt, newTermName("setOriginal"))
+ val reifiedOriginal = reify(tt.original)
+ rtt = Apply(setOriginal, List(reifiedOriginal))
+ }
+ rtt
+ }
case ta @ TypeApply(hk, ts) =>
- val thereAreOnlyTTs = ts collect { case t if !t.isInstanceOf[TypeTree] => t } isEmpty;
- val ttsAreNotEssential = ts collect { case tt: TypeTree => tt } find { tt => tt.original != null } isEmpty;
- if (thereAreOnlyTTs && ttsAreNotEssential) reifyTree(hk) else reifyProduct(ta)
+ if (ts exists isErased) reifyTree(hk) else reifyProduct(ta)
case global.emptyValDef =>
mirrorSelect(nme.emptyValDef)
+ case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) =>
+ CannotReifyClassOfBoundType(tree, tpe)
+ case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym =>
+ CannotReifyClassOfBoundEnum(tree, constant.tpe)
case _ =>
- if (tree.isDef)
+ if (tree.isDef) {
+ if (reifyDebug) println("boundSym: " + tree.symbol)
boundSyms += tree.symbol
+ }
reifyProduct(tree)
/*
@@ -415,8 +461,19 @@ abstract class LiftCode extends Transform with TypingTransformers {
* Reify a free reference. The result will be either a mirror reference
* to a global value, or else a mirror Literal.
*/
- private def reifyFree(tree: Tree): Tree =
- mirrorCall(nme.Ident, reifySymRef(tree.symbol))
+ private def reifyFree(tree: Tree): Tree = tree match {
+ case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass =>
+ val sym = tree.symbol
+ if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
+ if (reifyDebug) println("Free: " + sym)
+ val freeVar = mirrorCall("freeVar", reify(sym.name.toString), reify(sym.tpe), This(sym))
+ mirrorCall(nme.Ident, freeVar)
+ case This(_) =>
+ if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
+ mirrorCall(nme.This, reifySymRef(tree.symbol))
+ case _ =>
+ mirrorCall(nme.Ident, reifySymRef(tree.symbol))
+ }
// todo: consider whether we should also reify positions
private def reifyPosition(pos: Position): Tree =
@@ -495,8 +552,20 @@ abstract class LiftCode extends Transform with TypingTransformers {
symDefs.toList ++ fillIns.toList
}
+ }
+
+ /** A throwable signalling a reification error */
+ class ReifierError(var pos: Position, val msg: String) extends Throwable(msg) {
+ def this(msg: String) = this(NoPosition, msg)
+ }
+
+ def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = {
+ val msg = "cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe)
+ throw new ReifierError(tree.pos, msg)
+ }
- private def cannotReify(value: Any): Nothing =
- abort("don't know how to reify " + value + " of " + value.getClass)
+ def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = {
+ val msg = "cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe)
+ throw new ReifierError(tree.pos, msg)
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index 7d49c98dfb..c1265b39d7 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -1711,8 +1711,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
private def makeArguments(fun: Symbol, vparams: List[Symbol]): List[Tree] = (
//! TODO: make sure the param types are seen from the right prefix
- for ((tp, arg) <- fun.info.paramTypes zip vparams) yield
- gen.maybeMkAsInstanceOf(Ident(arg), tp, arg.tpe)
+ map2(fun.info.paramTypes, vparams)((tp, arg) => gen.maybeMkAsInstanceOf(Ident(arg), tp, arg.tpe))
)
private def findSpec(tp: Type): Type = tp match {
case TypeRef(pre, sym, _ :: _) => specializedType(tp)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index eaf1b1ffbc..036e7fc750 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -625,7 +625,7 @@ trait Implicits {
if (context.hasErrors)
fail("typing TypeApply reported errors for the implicit tree")
else {
- val result = new SearchResult(checked, subst)
+ val result = new SearchResult(itree2, subst)
incCounter(foundImplicits)
printInference("[success] found %s for pt %s".format(result, ptInstantiated))
result
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
index 44579400ff..6d31243fd0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
@@ -7,8 +7,7 @@ package scala.tools.nsc
package typechecker
import symtab._
-import Flags.{ CASE => _, _ }
-
+import Flags.{MUTABLE, METHOD, LABEL, SYNTHETIC}
/** Translate pattern matching into method calls (these methods form a zero-plus monad), similar in spirit to how for-comprehensions are compiled.
*
@@ -16,29 +15,16 @@ import Flags.{ CASE => _, _ }
* (lifting the body of the case into the monad using `one`).
*
* Cases are combined into a pattern match using the `orElse` combinator (the implicit failure case is expressed using the monad's `zero`).
- *
- * The monad `M` in which the pattern match is interpreted is determined by solving `implicitly[MatchingStrategy[M]]` for M.
- * Predef provides the default, `OptionMatching`
-
- * Example translation: TODO
-
- scrut match { case Person(father@Person(_, fatherName), name) if fatherName == name => }
- scrut match { case Person(father, name) => father match {case Person(_, fatherName) => }}
- Person.unapply(scrut) >> ((father, name) => (Person.unapply(father) >> (_, fatherName) => check(fatherName == name) >> (_ => body)))
-
- (a => (Person.unapply(a).>>(
- b => Person.unapply(b._1).>>(
- c => check(c._2 == b._2).>>(
- d => body)))))(scrut)
-
-TODO:
- - optimizer loops on virtpatmat compiler?
-
- - don't orElse a failure case at the end if there's a default case
- - implement spec more closely (see TODO's below)
- - fix inlining of methods in nested objects
+ * TODO:
+ * - interaction with CPS
+ * - Array patterns
+ * - implement spec more closely (see TODO's)
+ * - DCE
+ * - use manifests for type testing
+ *
* (longer-term) TODO:
+ * - user-defined unapplyProd
* - recover GADT typing by locally inserting implicit witnesses to type equalities derived from the current case, and considering these witnesses during subtyping (?)
* - recover exhaustivity and unreachability checking using a variation on the type-safe builder pattern
*/
@@ -46,26 +32,90 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
import global._
import definitions._
- class MatchTranslator(typer: Typer) extends MatchCodeGen {
- def typed(tree: Tree, mode: Int, pt: Type): Tree = typer.typed(tree, mode, pt) // for MatchCodeGen -- imports don't provide implementations for abstract members
+ object vpmName {
+ val one = newTermName("one")
+ val drop = newTermName("drop")
+ val flatMap = newTermName("flatMap")
+ val get = newTermName("get")
+ val guard = newTermName("guard")
+ val isEmpty = newTermName("isEmpty")
+ val orElse = newTermName("orElse")
+ val outer = newTermName("<outer>")
+ val runOrElse = newTermName("runOrElse")
+ val zero = newTermName("zero")
+ val __match = newTermName("__match")
+
+ def counted(str: String, i: Int) = newTermName(str+i)
+ }
+
+ object MatchTranslator {
+ def apply(typer: Typer): MatchTranslation = {
+ import typer._
+ // typing `__match` to decide which MatchTranslator to create adds 4% to quick.comp.timer
+ newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match {
+ case SilentResultValue(ms) => new PureMatchTranslator(typer, ms)
+ case _ => new OptimizingMatchTranslator(typer)
+ }
+ }
+ }
- import typer._
- import typeDebug.{ ptTree, ptBlock, ptLine }
+ class PureMatchTranslator(val typer: Typer, val matchStrategy: Tree) extends MatchTranslation with TreeMakers with PureCodegen
+ class OptimizingMatchTranslator(val typer: Typer) extends MatchTranslation with TreeMakers with MatchOptimizations
- def solveContextBound(contextBoundTp: Type): (Tree, Type) = {
- val solSym = NoSymbol.newTypeParameter(newTypeName("SolveImplicit$"))
- val param = solSym.setInfo(contextBoundTp.typeSymbol.typeParams(0).info.cloneInfo(solSym)) // TypeBounds(NothingClass.typeConstructor, baseTp)
- val pt = appliedType(contextBoundTp, List(param.tpeHK))
- val savedUndets = context.undetparams
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// talking to userland
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- context.undetparams = param :: context.undetparams
- val result = inferImplicit(EmptyTree, pt, false, false, context)
- context.undetparams = savedUndets
+ /** Interface with user-defined match monad?
+ * if there's a `__match` in scope, we use this as the match strategy, assuming it conforms to MatchStrategy as defined below:
+
+ type Matcher[P[_], M[+_], A] = {
+ def flatMap[B](f: P[A] => M[B]): M[B]
+ def orElse[B >: A](alternative: => M[B]): M[B]
+ }
+
+ abstract class MatchStrategy[P[_], M[+_]] {
+ // runs the matcher on the given input
+ def runOrElse[T, U](in: P[T])(matcher: P[T] => M[U]): P[U]
+
+ def zero: M[Nothing]
+ def one[T](x: P[T]): M[T]
+ def guard[T](cond: P[Boolean], then: => P[T]): M[T]
+ def isSuccess[T, U](x: P[T])(f: P[T] => M[U]): P[Boolean] // used for isDefinedAt
+ }
+
+ * P and M are derived from one's signature (`def one[T](x: P[T]): M[T]`)
+
+
+ * if no `__match` is found, we assume the following implementation (and generate optimized code accordingly)
+
+ object __match extends MatchStrategy[({type Id[x] = x})#Id, Option] {
+ def zero = None
+ def one[T](x: T) = Some(x)
+ // NOTE: guard's return type must be of the shape M[T], where M is the monad in which the pattern match should be interpreted
+ def guard[T](cond: Boolean, then: => T): Option[T] = if(cond) Some(then) else None
+ def runOrElse[T, U](x: T)(f: T => Option[U]): U = f(x) getOrElse (throw new MatchError(x))
+ def isSuccess[T, U](x: T)(f: T => Option[U]): Boolean = !f(x).isEmpty
+ }
+
+ */
+ trait MatchMonadInterface {
+ val typer: Typer
+ val matchOwner = typer.context.owner
+
+ def inMatchMonad(tp: Type): Type
+ def pureType(tp: Type): Type
+ final def matchMonadResult(tp: Type): Type =
+ tp.baseType(matchMonadSym).typeArgs match {
+ case arg :: Nil => arg
+ case _ => ErrorType
+ }
- (result.tree, result.subst.to(result.subst.from indexOf param))
- }
+ protected def matchMonadSym: Symbol
+ }
- lazy val (matchingStrategy, matchingMonadType) = solveContextBound(MatchingStrategyClass.typeConstructor)
+ trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore =>
+ import typer.{typed, context, silent, reallyExists}
/** Implement a pattern match by turning its cases (including the implicit failure case)
* into the corresponding (monadic) extractors, and combining them with the `orElse` combinator.
@@ -75,7 +125,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
*
* NOTE: the resulting tree is not type checked, nor are nested pattern matches transformed
* thus, you must typecheck the result (and that will in turn translate nested matches)
- * this could probably optimized... (but note that the matchingStrategy must be solved for each nested patternmatch)
+ * this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch)
*/
def translateMatch(scrut: Tree, cases: List[CaseDef], pt: Type): Tree = {
// we don't transform after typers
@@ -83,12 +133,17 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// and the only place that emits Matches after typers is for exception handling anyway)
assert(phase.id <= currentRun.typerPhase.id, phase)
+ def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
+ case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args)
+ case _ => tp
+ }
+
val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen))
- val scrutSym = freshSym(scrut.pos, scrutType)
+ val scrutSym = freshSym(scrut.pos, pureType(scrutType))
val okPt = repeatedToSeq(pt)
// pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental
- combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, context.owner)
+ combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner)
}
@@ -139,6 +194,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// must use type `tp`, which is provided by extractor's result, not the type expected by binder,
// as b.info may be based on a Typed type ascription, which has not been taken into account yet by the translation
// (it will later result in a type test when `tp` is not a subtype of `b.info`)
+ // TODO: can we simplify this, together with the Bound case?
(extractor.subPatBinders, extractor.subPatTypes).zipped foreach { case (b, tp) => b setInfo tp } // println("changing "+ b +" : "+ b.info +" -> "+ tp);
// println("translateExtractorPattern checking parameter type: "+ (patBinder, patBinder.info.widen, extractor.paramType, patBinder.info.widen <:< extractor.paramType))
@@ -215,12 +271,8 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
and it binds the variable name to that value.
**/
case Bound(subpatBinder, p) =>
- // TreeMaker with empty list of trees only performs the substitution subpatBinder --> patBinder
- // println("rebind "+ subpatBinder +" to "+ patBinder)
- withSubPats(List(SubstOnlyTreeMaker(Substitution(subpatBinder, CODE.REF(patBinder)))),
- // the symbols are markers that may be used to refer to the result of the extractor in which the corresponding tree is nested
- // it's the responsibility of the treemaker to replace this symbol by a reference that
- // selects that result on the function symbol of the flatMap call that binds to the result of this extractor
+ // replace subpatBinder by patBinder (as if the Bind was not there)
+ withSubPats(List(SubstOnlyTreeMaker(subpatBinder, patBinder)),
// must be patBinder, as subpatBinder has the wrong info: even if the bind assumes a better type, this is not guaranteed until we cast
(patBinder, p)
)
@@ -266,7 +318,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
if (guard == EmptyTree) Nil
else List(GuardTreeMaker(guard))
- // TODO: 1) if we want to support a generalisation of Kotlin's patmat continue, must not hard-wire lifting into the monad (which is now done by pmgen.one),
+ // TODO: 1) if we want to support a generalisation of Kotlin's patmat continue, must not hard-wire lifting into the monad (which is now done by codegen.one),
// so that user can generate failure when needed -- use implicit conversion to lift into monad on-demand?
// to enable this, probably need to move away from Option to a monad specific to pattern-match,
// so that we can return Option's from a match without ambiguity whether this indicates failure in the monad, or just some result in the monad
@@ -379,34 +431,32 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
protected lazy val expectedLength = lastIndexingBinder - firstIndexingBinder + 1
protected lazy val minLenToCheck = if(lastIsStar) 1 else 0
protected def seqTree(binder: Symbol) = tupleSel(binder)(firstIndexingBinder+1)
- protected def tupleSel(binder: Symbol)(i: Int): Tree = pmgen.tupleSel(binder)(i)
+ protected def tupleSel(binder: Symbol)(i: Int): Tree = codegen.tupleSel(binder)(i)
// the trees that select the subpatterns on the extractor's result, referenced by `binder`
// require isSeq
protected def subPatRefsSeq(binder: Symbol): List[Tree] = {
- // only relevant if isSeq: (here to avoid capturing too much in the returned closure)
- val indexingIndices = (0 to (lastIndexingBinder-firstIndexingBinder))
- val nbIndexingIndices = indexingIndices.length
+ val indexingIndices = (0 to (lastIndexingBinder-firstIndexingBinder))
+ val nbIndexingIndices = indexingIndices.length
- // this error is checked by checkStarPatOK
- // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == nbSubPats, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats))
+ // this error-condition has already been checked by checkStarPatOK:
+ // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == nbSubPats, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats))
// there are `firstIndexingBinder` non-seq tuple elements preceding the Seq
(((1 to firstIndexingBinder) map tupleSel(binder)) ++
// then we have to index the binder that represents the sequence for the remaining subpatterns, except for...
- (indexingIndices map pmgen.index(seqTree(binder))) ++
+ (indexingIndices map codegen.index(seqTree(binder))) ++
// the last one -- if the last subpattern is a sequence wildcard: drop the prefix (indexed by the refs on the line above), return the remainder
(if(!lastIsStar) Nil else List(
if(nbIndexingIndices == 0) seqTree(binder)
- else pmgen.drop(seqTree(binder))(nbIndexingIndices)))).toList
+ else codegen.drop(seqTree(binder))(nbIndexingIndices)))).toList
}
// the trees that select the subpatterns on the extractor's result, referenced by `binder`
// require (nbSubPats > 0 && (!lastIsStar || isSeq))
- protected def subPatRefs(binder: Symbol): List[Tree] = {
+ protected def subPatRefs(binder: Symbol): List[Tree] =
if (nbSubPats == 0) Nil
else if (isSeq) subPatRefsSeq(binder)
else ((1 to nbSubPats) map tupleSel(binder)).toList
- }
protected def lengthGuard(binder: Symbol): Option[Tree] =
// no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied
@@ -427,7 +477,9 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
}
}
- // TODO: to be called when there's a def unapplyProd(x: T): Product_N
+ // TODO: to be called when there's a def unapplyProd(x: T): U
+ // U must have N members _1,..., _N -- the _i are type checked, call their type Ti,
+ //
// for now only used for case classes -- pretending there's an unapplyProd that's the identity (and don't call it)
class ExtractorCallProd(fun: Tree, args: List[Tree]) extends ExtractorCall(args) {
// TODO: fix the illegal type bound in pos/t602 -- type inference messes up before we get here:
@@ -439,15 +491,15 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// private val extractorTp = if (wellKinded(fun.tpe)) fun.tpe else existentialAbstraction(origExtractorTp.typeParams, origExtractorTp.resultType)
// println("ExtractorCallProd: "+ (fun.tpe, existentialAbstraction(origExtractorTp.typeParams, origExtractorTp.resultType)))
// println("ExtractorCallProd: "+ (fun.tpe, args map (_.tpe)))
- private def extractorTp = fun.tpe
+ private def constructorTp = fun.tpe
def isTyped = fun.isTyped
// to which type should the previous binder be casted?
- def paramType = extractorTp.finalResultType
+ def paramType = constructorTp.finalResultType
def isSeq: Boolean = rawSubPatTypes.nonEmpty && isRepeatedParamType(rawSubPatTypes.last)
- protected def rawSubPatTypes = extractorTp.paramTypes
+ protected def rawSubPatTypes = constructorTp.paramTypes
// binder has type paramType
def treeMaker(binder: Symbol, pos: Position): TreeMaker = {
@@ -456,31 +508,20 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
}
/* TODO: remove special case when the following bug is fixed
-scala> :paste
-// Entering paste mode (ctrl-D to finish)
-
class Foo(x: Other) { x._1 } // BUG: can't refer to _1 if its defining class has not been type checked yet
case class Other(y: String)
-
-// Exiting paste mode, now interpreting.
-
-<console>:8: error: value _1 is not a member of Other
- class Foo(x: Other) { x._1 }
- ^
-
-scala> case class Other(y: String)
-defined class Other
-
-scala> class Foo(x: Other) { x._1 }
-defined class Foo */
+-- this is ok:
+case class Other(y: String)
+class Foo(x: Other) { x._1 } // no error in this order
+*/
override protected def tupleSel(binder: Symbol)(i: Int): Tree = { import CODE._
// reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component
val caseAccs = binder.info.typeSymbol.caseFieldAccessors
if (caseAccs isDefinedAt (i-1)) REF(binder) DOT caseAccs(i-1)
- else pmgen.tupleSel(binder)(i)
+ else codegen.tupleSel(binder)(i)
}
- override def toString(): String = "case class "+ (if (extractorTp eq null) fun else paramType.typeSymbol) +" with arguments "+ args
+ override def toString(): String = "case class "+ (if (constructorTp eq null) fun else paramType.typeSymbol) +" with arguments "+ args
}
class ExtractorCallRegular(extractorCallIncludingDummy: Tree, args: List[Tree]) extends ExtractorCall(args) {
@@ -495,7 +536,7 @@ defined class Foo */
def treeMaker(patBinderOrCasted: Symbol, 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, 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
+ 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, Substitution(subPatBinders, subPatRefs(binder)))(resultType.typeSymbol == BooleanClass)
}
@@ -524,12 +565,7 @@ defined class Foo */
// turn an extractor's result type into something `monadTypeToSubPatTypesAndRefs` understands
protected lazy val resultInMonad: Type = if(!hasLength(tpe.paramTypes, 1)) ErrorType else {
if (resultType.typeSymbol == BooleanClass) UnitClass.tpe
- else {
- val monadArgs = resultType.baseType(matchingMonadType.typeSymbol).typeArgs
- // assert(monadArgs.length == 1, "unhandled extractor type: "+ extractorTp) // TODO: overloaded unapply??
- if(monadArgs.length == 1) monadArgs(0)
- else ErrorType
- }
+ else matchMonadResult(resultType)
}
protected lazy val rawSubPatTypes =
@@ -543,81 +579,6 @@ defined class Foo */
override def toString() = extractorCall +": "+ extractorCall.tpe +" (symbol= "+ extractorCall.symbol +")."
}
- // tack an outer test onto `cond` if binder.info and expectedType warrant it
- def maybeWithOuterCheck(binder: Symbol, expectedTp: Type)(cond: Tree): Tree = { import CODE._
- if ( !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass)
- && needsOuterTest(expectedTp, binder.info, context.owner)) {
- val expectedPrefix = expectedTp.prefix match {
- case ThisType(clazz) => THIS(clazz)
- case pre => REF(pre.prefix, pre.termSymbol)
- }
-
- // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix`
- // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor?
- val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC
- val outerCheck = (Select(pmgen._asInstanceOf(binder, expectedTp), outer)) OBJ_EQ expectedPrefix
-
- // first check cond, since that should ensure we're not selecting outer on null
- pmgen.and(cond, outerCheck)
- }
- else
- cond
- }
-
- // TODO: also need to test when erasing pt loses crucial information (and if we can recover it using a manifest)
- def needsTypeTest(tp: Type, pt: Type) = !(tp <:< pt)
- def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(pmgen._isInstanceOf(binder, pt))
-
- /** Type patterns consist of types, type variables, and wildcards. A type pattern T is of one of the following forms:
- - A reference to a class C, p.C, or T#C.
- This type pattern matches any non-null instance of the given class.
- Note that the prefix of the class, if it is given, is relevant for determining class instances.
- For instance, the pattern p.C matches only instances of classes C which were created with the path p as prefix.
- The bottom types scala.Nothing and scala.Null cannot be used as type patterns, because they would match nothing in any case.
-
- - A singleton type p.type.
- This type pattern matches only the value denoted by the path p
- (that is, a pattern match involved a comparison of the matched value with p using method eq in class AnyRef). // TODO: the actual pattern matcher uses ==, so that's what I'm using for now
- // https://issues.scala-lang.org/browse/SI-4577 "pattern matcher, still disappointing us at equality time"
-
- - A compound type pattern T1 with ... with Tn where each Ti is a type pat- tern.
- This type pattern matches all values that are matched by each of the type patterns Ti.
-
- - A parameterized type pattern T[a1,...,an], where the ai are type variable patterns or wildcards _.
- This type pattern matches all values which match T for some arbitrary instantiation of the type variables and wildcards.
- The bounds or alias type of these type variable are determined as described in (§8.3).
-
- - A parameterized type pattern scala.Array[T1], where T1 is a type pattern. // TODO
- This type pattern matches any non-null instance of type scala.Array[U1], where U1 is a type matched by T1.
- **/
-
- // generate the tree for the run-time test that follows from the fact that
- // a `scrut` of known type `scrutTp` is expected to have type `expectedTp`
- // uses maybeWithOuterCheck to check the type's prefix
- def typeAndEqualityTest(patBinder: Symbol, pt: Type): Tree = { import CODE._
- // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null`
- // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false")
- def genEqualsAndInstanceOf(sym: Symbol): Tree
- = pmgen._equals(REF(sym), patBinder) AND pmgen._isInstanceOf(patBinder, pt.widen)
-
- def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe
-
- val patBinderTp = patBinder.info.widen
- def isMatchUnlessNull = isRefTp(pt) && !needsTypeTest(patBinderTp, pt)
-
- // TODO: [SPEC] type test for Array
- // TODO: use manifests to improve tests (for erased types we can do better when we have a manifest)
- pt match {
- case SingleType(_, sym) /*this implies sym.isStable*/ => genEqualsAndInstanceOf(sym) // TODO: [SPEC] the spec requires `eq` instead of `==` here
- case ThisType(sym) if sym.isModule => genEqualsAndInstanceOf(sym) // must use == to support e.g. List() == Nil
- case ThisType(sym) => REF(patBinder) OBJ_EQ This(sym)
- case ConstantType(Constant(null)) if isRefTp(patBinderTp) => REF(patBinder) OBJ_EQ NULL
- case ConstantType(const) => pmgen._equals(Literal(const), patBinder)
- case _ if isMatchUnlessNull => maybeWithOuterCheck(patBinder, pt)(REF(patBinder) OBJ_NE NULL)
- case _ => typeTest(patBinder, pt)
- }
- }
-
/** A conservative approximation of which patterns do not discern anything.
* They are discarded during the translation.
*/
@@ -643,14 +604,74 @@ defined class Foo */
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// substitution
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ trait TypedSubstitution extends MatchMonadInterface {
+ object Substitution {
+ def apply(from: Symbol, to: Tree) = new Substitution(List(from), List(to))
+ // requires sameLength(from, to)
+ def apply(from: List[Symbol], to: List[Tree]) =
+ if (from nonEmpty) new Substitution(from, to) else EmptySubstitution
+ }
+
+ class Substitution(val from: List[Symbol], val to: List[Tree]) {
+ // We must explicitly type the trees that we replace inside some other tree, since the latter may already have been typed,
+ // and will thus not be retyped. This means we might end up with untyped subtrees inside bigger, typed trees.
+ def apply(tree: Tree): Tree = {
+ // according to -Ystatistics 10% of translateMatch's time is spent in this method...
+ // since about half of the typedSubst's end up being no-ops, the check below shaves off 5% of the time spent in typedSubst
+ if (!tree.exists { case i@Ident(_) => from contains i.symbol case _ => false}) tree
+ else (new Transformer {
+ @inline private def typedIfOrigTyped(to: Tree, origTp: Type): Tree =
+ if (origTp == null || origTp == NoType) to
+ // important: only type when actually substing and when original tree was typed
+ // (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors)
+ else typer.typed(to, EXPRmode, WildcardType)
+
+ override def transform(tree: Tree): Tree = {
+ def subst(from: List[Symbol], to: List[Tree]): Tree =
+ if (from.isEmpty) tree
+ else if (tree.symbol == from.head) typedIfOrigTyped(to.head.shallowDuplicate, tree.tpe)
+ else subst(from.tail, to.tail)
+
+ tree match {
+ case Ident(_) => subst(from, to)
+ case _ => super.transform(tree)
+ }
+ }
+ }).transform(tree)
+ }
+
+
+ // the substitution that chains `other` before `this` substitution
+ // forall t: Tree. this(other(t)) == (this >> other)(t)
+ def >>(other: Substitution): Substitution = {
+ val (fromFiltered, toFiltered) = (from, to).zipped filter { (f, t) => !other.from.contains(f) }
+ new Substitution(other.from ++ fromFiltered, other.to.map(apply) ++ toFiltered) // a quick benchmarking run indicates the `.map(apply)` is not too costly
+ }
+ override def toString = (from zip to) mkString("Substitution(", ", ", ")")
+ }
+
+ object EmptySubstitution extends Substitution(Nil, Nil) {
+ override def apply(tree: Tree): Tree = tree
+ override def >>(other: Substitution): Substitution = other
+ }
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// the making of the trees
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ trait TreeMakers extends TypedSubstitution { self: CodegenCore =>
+ def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) =
+ (cases, Nil)
- trait TreeMakers {
- def inMatchMonad(tp: Type): Type = appliedType(matchingMonadType, List(tp))
- lazy val optimizingCodeGen = matchingMonadType.typeSymbol eq OptionClass
+ def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] =
+ None
abstract class TreeMaker {
+ /** captures the scope and the value of the bindings in patterns
+ * important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed)
+ */
def substitution: Substitution =
if (currSub eq null) localSubstitution
else currSub
@@ -668,7 +689,6 @@ defined class Foo */
// build Tree that chains `next` after the current extractor
def chainBefore(next: Tree, pt: Type): Tree
- def treesToHoist: List[Tree] = Nil
}
case class TrivialTreeMaker(tree: Tree) extends TreeMaker {
@@ -679,71 +699,30 @@ defined class Foo */
case class BodyTreeMaker(body: Tree, matchPt: Type) extends TreeMaker {
val localSubstitution: Substitution = EmptySubstitution
def chainBefore(next: Tree, pt: Type): Tree = // assert(next eq EmptyTree)
- atPos(body.pos)(substitution(pmgen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here
+ atPos(body.pos)(substitution(codegen.one(body, body.tpe, matchPt))) // since SubstOnly treemakers are dropped, need to do it here
}
- case class SubstOnlyTreeMaker(localSubstitution: Substitution) extends TreeMaker {
+ case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker {
+ val localSubstitution = Substitution(prevBinder, CODE.REF(nextBinder))
def chainBefore(next: Tree, pt: Type): Tree = substitution(next)
}
abstract class FunTreeMaker extends TreeMaker {
val nextBinder: Symbol
-
- // for CSE (used iff optimizingCodeGen)
- // TODO: factor this out -- don't mutate treemakers
- var reused: Boolean = false
- def reusedBinders: List[Symbol] = Nil
- override def treesToHoist: List[Tree] = { import CODE._
- reusedBinders map { b => VAL(b) === pmgen.mkZero(b.info) }
- }
}
- abstract class FreshFunTreeMaker extends FunTreeMaker {
+ abstract class CondTreeMaker extends FunTreeMaker {
val pos: Position
val prevBinder: Symbol
val nextBinderTp: Type
- lazy val nextBinder = freshSym(pos, nextBinderTp)
- lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder)))
- }
-
- // TODO: factor out optimization-specific stuff into codegen
- abstract class CondTreeMaker extends FreshFunTreeMaker { import CODE._
val cond: Tree
val res: Tree
- // for CSE (used iff optimizingCodeGen)
- // must set reused before!
- override lazy val reusedBinders = if(reused) List(freshSym(pos, BooleanClass.tpe, "rc") setFlag MUTABLE, nextBinder setFlag MUTABLE) else Nil
- def storedCond = reusedBinders(0)
- def storedRes = reusedBinders(1)
+ lazy val nextBinder = freshSym(pos, nextBinderTp)
+ lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder)))
def chainBefore(next: Tree, pt: Type): Tree =
- if (!reused)
- atPos(pos)(pmgen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next)))
- else { // for CSE (used iff optimizingCodeGen)
- IF (cond) THEN BLOCK(
- storedCond === TRUE,
- storedRes === res,
- substitution(next).duplicate // TODO: finer-grained dup'ing
- ) ELSE pmgen.zero
- }
- }
-
- // for CSE (used iff optimizingCodeGen)
- case class ReusingCondTreeMaker(dropped_priors: List[(TreeMaker, Option[TreeMaker])]) extends TreeMaker { import CODE._
- lazy val localSubstitution = {
- val (from, to) = dropped_priors.collect {case (dropped: CondTreeMaker, Some(prior: CondTreeMaker)) => (dropped.nextBinder, REF(prior.storedRes))}.unzip
- val oldSubs = dropped_priors.collect {case (dropped: TreeMaker, _) => dropped.substitution}
- oldSubs.foldLeft(Substitution(from, to))(_ >> _)
- }
-
- def chainBefore(next: Tree, pt: Type): Tree = {
- val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: CondTreeMaker)) => ctm}.get.storedCond)
-
- IF (cond) THEN BLOCK(
- substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S)
- ) ELSE pmgen.zero
- }
+ atPos(pos)(codegen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next)))
}
/**
@@ -754,12 +733,13 @@ defined class Foo */
* in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to`
*/
case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker {
- def chainBefore(next: Tree, pt: Type): Tree = atPos(extractor.pos)(
- if (extractorReturnsBoolean) pmgen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext(next)))
- else pmgen.flatMap(extractor, pmgen.fun(nextBinder, substitution(condAndNext(next))))
- )
-
- private def condAndNext(next: Tree): Tree = extraCond map (pmgen.condOptimized(_, next)) getOrElse next
+ def chainBefore(next: Tree, pt: Type): Tree = {
+ val condAndNext = extraCond map (codegen.ifThenElseZero(_, next)) getOrElse next
+ atPos(extractor.pos)(
+ if (extractorReturnsBoolean) codegen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext))
+ else codegen.flatMap(extractor, nextBinder, substitution(condAndNext))
+ )
+ }
override def toString = "X"+(extractor, nextBinder)
}
@@ -768,21 +748,42 @@ defined class Foo */
case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree], localSubstitution: Substitution) extends TreeMaker { import CODE._
def chainBefore(next: Tree, pt: Type): Tree = {
val nullCheck = REF(prevBinder) OBJ_NE NULL
- val cond = extraCond match {
- case None => nullCheck
- case Some(c) => nullCheck AND c
- }
- pmgen.condOptimized(cond, substitution(next))
+ val cond = extraCond map (nullCheck AND _) getOrElse nullCheck
+ codegen.ifThenElseZero(cond, substitution(next))
}
override def toString = "P"+(prevBinder, extraCond getOrElse "", localSubstitution)
}
+ // tack an outer test onto `cond` if binder.info and expectedType warrant it
+ def maybeWithOuterCheck(binder: Symbol, expectedTp: Type)(cond: Tree): Tree = { import CODE._
+ if ( !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass)
+ && needsOuterTest(expectedTp, binder.info, matchOwner)) {
+ val expectedPrefix = expectedTp.prefix match {
+ case ThisType(clazz) => THIS(clazz)
+ case pre => REF(pre.prefix, pre.termSymbol)
+ }
+
+ // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix`
+ // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor?
+ val outer = expectedTp.typeSymbol.newMethod(vpmName.outer) setInfo expectedTp.prefix setFlag SYNTHETIC
+ val outerCheck = (Select(codegen._asInstanceOf(binder, expectedTp), outer)) OBJ_EQ expectedPrefix
+
+ // first check cond, since that should ensure we're not selecting outer on null
+ codegen.and(cond, outerCheck)
+ }
+ else
+ cond
+ }
+
+ // TODO: also need to test when erasing pt loses crucial information (and if we can recover it using a manifest)
+ def needsTypeTest(tp: Type, pt: Type) = !(tp <:< pt)
+ private def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(codegen._isInstanceOf(binder, pt))
// need to substitute since binder may be used outside of the next extractor call (say, in the body of the case)
case class TypeTestTreeMaker(prevBinder: Symbol, nextBinderTp: Type, pos: Position) extends CondTreeMaker {
val cond = typeTest(prevBinder, nextBinderTp)
- val res = pmgen._asInstanceOf(prevBinder, nextBinderTp)
+ val res = codegen._asInstanceOf(prevBinder, nextBinderTp)
override def toString = "TT"+(prevBinder, nextBinderTp)
}
@@ -790,8 +791,58 @@ defined class Foo */
case class TypeAndEqualityTestTreeMaker(prevBinder: Symbol, patBinder: Symbol, pt: Type, pos: Position) extends CondTreeMaker {
val nextBinderTp = glb(List(patBinder.info.widen, pt))
+ /** Type patterns consist of types, type variables, and wildcards. A type pattern T is of one of the following forms:
+ - A reference to a class C, p.C, or T#C.
+ This type pattern matches any non-null instance of the given class.
+ Note that the prefix of the class, if it is given, is relevant for determining class instances.
+ For instance, the pattern p.C matches only instances of classes C which were created with the path p as prefix.
+ The bottom types scala.Nothing and scala.Null cannot be used as type patterns, because they would match nothing in any case.
+
+ - A singleton type p.type.
+ This type pattern matches only the value denoted by the path p
+ (that is, a pattern match involved a comparison of the matched value with p using method eq in class AnyRef). // TODO: the actual pattern matcher uses ==, so that's what I'm using for now
+ // https://issues.scala-lang.org/browse/SI-4577 "pattern matcher, still disappointing us at equality time"
+
+ - A compound type pattern T1 with ... with Tn where each Ti is a type pat- tern.
+ This type pattern matches all values that are matched by each of the type patterns Ti.
+
+ - A parameterized type pattern T[a1,...,an], where the ai are type variable patterns or wildcards _.
+ This type pattern matches all values which match T for some arbitrary instantiation of the type variables and wildcards.
+ The bounds or alias type of these type variable are determined as described in (§8.3).
+
+ - A parameterized type pattern scala.Array[T1], where T1 is a type pattern. // TODO
+ This type pattern matches any non-null instance of type scala.Array[U1], where U1 is a type matched by T1.
+ **/
+
+ // generate the tree for the run-time test that follows from the fact that
+ // a `scrut` of known type `scrutTp` is expected to have type `expectedTp`
+ // uses maybeWithOuterCheck to check the type's prefix
+ private def typeAndEqualityTest(patBinder: Symbol, pt: Type): Tree = { import CODE._
+ // TODO: `null match { x : T }` will yield a check that (indirectly) tests whether `null ne null`
+ // don't bother (so that we don't end up with the warning "comparing values of types Null and Null using `ne' will always yield false")
+ def genEqualsAndInstanceOf(sym: Symbol): Tree
+ = codegen._equals(REF(sym), patBinder) AND codegen._isInstanceOf(patBinder, pt.widen)
+
+ def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe
+
+ val patBinderTp = patBinder.info.widen
+ def isMatchUnlessNull = isRefTp(pt) && !needsTypeTest(patBinderTp, pt)
+
+ // TODO: [SPEC] type test for Array
+ // TODO: use manifests to improve tests (for erased types we can do better when we have a manifest)
+ pt match {
+ case SingleType(_, sym) /*this implies sym.isStable*/ => genEqualsAndInstanceOf(sym) // TODO: [SPEC] the spec requires `eq` instead of `==` here
+ case ThisType(sym) if sym.isModule => genEqualsAndInstanceOf(sym) // must use == to support e.g. List() == Nil
+ case ThisType(sym) => REF(patBinder) OBJ_EQ This(sym)
+ case ConstantType(Constant(null)) if isRefTp(patBinderTp) => REF(patBinder) OBJ_EQ NULL
+ case ConstantType(const) => codegen._equals(Literal(const), patBinder)
+ case _ if isMatchUnlessNull => maybeWithOuterCheck(patBinder, pt)(REF(patBinder) OBJ_NE NULL)
+ case _ => typeTest(patBinder, pt)
+ }
+ }
+
val cond = typeAndEqualityTest(patBinder, pt)
- val res = pmgen._asInstanceOf(patBinder, nextBinderTp)
+ val res = codegen._asInstanceOf(patBinder, nextBinderTp)
override def toString = "TET"+(patBinder, pt)
}
@@ -801,7 +852,7 @@ defined class Foo */
// NOTE: generate `patTree == patBinder`, since the extractor must be in control of the equals method (also, patBinder may be null)
// equals need not be well-behaved, so don't intersect with pattern's (stabilized) type (unlike MaybeBoundTyped's accumType, where it's required)
- val cond = pmgen._equals(patTree, prevBinder)
+ val cond = codegen._equals(patTree, prevBinder)
val res = CODE.REF(prevBinder)
override def toString = "ET"+(prevBinder, patTree)
}
@@ -836,7 +887,7 @@ defined class Foo */
if (canDuplicate) {
altss map {altTreeMakers =>
combineExtractors(altTreeMakers :+ TrivialTreeMaker(substitution(next).duplicate), pt)
- } reduceLeft pmgen.typedOrElse(pt)
+ } reduceLeft codegen.typedOrElse(pt)
} else {
val rest = freshSym(pos, functionType(List(), inMatchMonad(pt)), "rest")
// rest.info.member(nme.apply).withAnnotation(AnnotationInfo(ScalaInlineClass.tpe, Nil, Nil))
@@ -848,7 +899,7 @@ defined class Foo */
)
BLOCK(
VAL(rest) === Function(Nil, substitution(next)),
- combinedAlts reduceLeft pmgen.typedOrElse(pt)
+ combinedAlts reduceLeft codegen.typedOrElse(pt)
)
}
)
@@ -857,14 +908,239 @@ defined class Foo */
case class GuardTreeMaker(guardTree: Tree) extends TreeMaker {
val localSubstitution: Substitution = EmptySubstitution
- def chainBefore(next: Tree, pt: Type): Tree = pmgen.flatMapGuard(substitution(guardTree), next)
+ def chainBefore(next: Tree, pt: Type): Tree = codegen.flatMapGuard(substitution(guardTree), next)
override def toString = "G("+ guardTree +")"
}
+ def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker])
+
+ // a foldLeft to accumulate the localSubstitution left-to-right
+ // it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution
+ def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = {
+ var accumSubst: Substitution = initial
+ treeMakers foreach { maker =>
+ maker incorporateOuterSubstitution accumSubst
+ accumSubst = maker.substitution
+ }
+ removeSubstOnly(treeMakers)
+ }
+
+ // calls propagateSubstitution on the treemakers
+ def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){
+ val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
+
+ emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{
+ val (matcher, hasDefault, toHoist) =
+ if (casesUnOpt nonEmpty) {
+ // when specified, need to propagate pt explicitly (type inferencer can't handle it)
+ val optPt =
+ if (isFullyDefined(pt)) inMatchMonad(pt)
+ else NoType
+
+ // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
+ // exhaustivity and reachability must be checked before optimization as well
+ val hasDefault = casesUnOpt.nonEmpty && {
+ val nonTrivLast = casesUnOpt.last
+ nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker]
+ }
+
+ val (cases, toHoist) = optimizeCases(scrutSym, casesUnOpt, pt)
+
+ val combinedCases =
+ cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt))
+
+ (combinedCases, hasDefault, toHoist)
+ } else (codegen.zero, false, Nil)
+
+ val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault)
+ if (toHoist isEmpty) expr
+ else Block(toHoist, expr)
+ }
+ }
+
+ // combineExtractors changes the current substitution's of the tree makers in `treeMakers`
+ // requires propagateSubstitution(treeMakers) has been called
+ def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree =
+ treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt))
+
+ // TODO: do this during tree construction, but that will require tracking the current owner in treemakers
+ // TODO: assign more fine-grained positions
+ // fixes symbol nesting, assigns positions
+ private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
+ currentOwner = origOwner
+
+ override def traverse(t: Tree) {
+ if (t != EmptyTree && t.pos == NoPosition) {
+ t.setPos(pos)
+ }
+ t match {
+ case Function(_, _) if t.symbol == NoSymbol =>
+ t.symbol = currentOwner.newAnonymousFunctionValue(t.pos)
+ // println("new symbol for "+ (t, t.symbol.ownerChain))
+ case Function(_, _) if (t.symbol.owner == NoSymbol) || (t.symbol.owner == origOwner) =>
+ // println("fundef: "+ (t, t.symbol.ownerChain, currentOwner.ownerChain))
+ t.symbol.owner = currentOwner
+ case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2)
+ // println("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain))
+ if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree??
+ assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner, d.symbol.lazyAccessor)
+ d.symbol.lazyAccessor.owner = currentOwner
+ }
+ if(d.symbol.moduleClass ne NoSymbol)
+ d.symbol.moduleClass.owner = currentOwner
+
+ d.symbol.owner = currentOwner
+ // case _ if (t.symbol != NoSymbol) && (t.symbol ne null) =>
+ // println("untouched "+ (t, t.getClass, t.symbol.ownerChain, currentOwner.ownerChain))
+ case _ =>
+ }
+ super.traverse(t)
+ }
+
+ // override def apply
+ // println("before fixerupper: "+ xTree)
+ // currentRun.trackerFactory.snapshot()
+ // println("after fixerupper")
+ // currentRun.trackerFactory.snapshot()
+ }
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// generate actual trees
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ trait CodegenCore extends MatchMonadInterface {
+ private var ctr = 0
+ def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = {ctr += 1;
+ // assert(owner ne null)
+ // assert(owner ne NoSymbol)
+ NoSymbol.newTermSymbol(vpmName.counted(prefix, ctr), pos) setInfo repackExistential(tp)
+ }
+
+ // codegen relevant to the structure of the translation (how extractors are combined)
+ trait AbsCodegen {
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree
+ def one(res: Tree, bodyPt: Type, matchPt: Type): Tree
+ def zero: Tree
+ def flatMap(prev: Tree, b: Symbol, next: Tree): Tree
+ def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree
+
+ def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree
+ def flatMapGuard(cond: Tree, next: Tree): Tree
+
+ def fun(arg: Symbol, body: Tree): Tree
+ def ifThenElseZero(c: Tree, then: Tree): Tree
+ def _equals(checker: Tree, binder: Symbol): Tree
+ def _asInstanceOf(b: Symbol, tp: Type): Tree
+ def mkZero(tp: Type): Tree
+
+ def tupleSel(binder: Symbol)(i: Int): Tree
+ def index(tgt: Tree)(i: Int): Tree
+ def drop(tgt: Tree)(n: Int): Tree
+ def and(a: Tree, b: Tree): Tree
+ def _isInstanceOf(b: Symbol, tp: Type): Tree
+ }
+
+ def codegen: AbsCodegen
+
+ def typesConform(tp: Type, pt: Type) = ((tp eq pt) || (tp <:< pt))
+
+ abstract class CommonCodegen extends AbsCodegen { import CODE._
+ def fun(arg: Symbol, body: Tree): Tree = Function(List(ValDef(arg)), body)
+ def genTypeApply(tfun: Tree, args: Type*): Tree = if(args contains NoType) tfun else TypeApply(tfun, args.toList map TypeTree)
+ def tupleSel(binder: Symbol)(i: Int): Tree = (REF(binder) DOT nme.productAccessorName(i)) // make tree that accesses the i'th component of the tuple referenced by binder
+ def index(tgt: Tree)(i: Int): Tree = tgt APPLY (LIT(i))
+ def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n))
+ def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya
+ def and(a: Tree, b: Tree): Tree = a AND b
+ def ifThenElseZero(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero
+
+ // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly)
+ def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = repackExistential(tp)
+ if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX)
+ else gen.mkAsInstanceOf(t, tpX, true, false)
+ }
+
+ def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false)
+ // { val tpX = repackExistential(tp)
+ // if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE }
+ // else gen.mkIsInstanceOf(REF(b), tpX, true, false)
+ // }
+
+ def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = repackExistential(tp)
+ if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX)
+ else gen.mkAsInstanceOf(REF(b), tpX, true, false)
+ }
+
+ // duplicated out of frustration with cast generation
+ def mkZero(tp: Type): Tree = {
+ tp.typeSymbol match {
+ case UnitClass => Literal(Constant())
+ case BooleanClass => Literal(Constant(false))
+ case FloatClass => Literal(Constant(0.0f))
+ case DoubleClass => Literal(Constant(0.0d))
+ case ByteClass => Literal(Constant(0.toByte))
+ case ShortClass => Literal(Constant(0.toShort))
+ case IntClass => Literal(Constant(0))
+ case LongClass => Literal(Constant(0L))
+ case CharClass => Literal(Constant(0.toChar))
+ case _ => gen.mkAsInstanceOf(Literal(Constant(null)), tp, any = true, wrapInApply = false) // the magic incantation is true/false here
+ }
+ }
+ }
+ }
+
+ trait PureMatchMonadInterface extends MatchMonadInterface {
+ val matchStrategy: Tree
+
+ def inMatchMonad(tp: Type): Type = appliedType(oneSig, List(tp)).finalResultType
+ def pureType(tp: Type): Type = appliedType(oneSig, List(tp)).paramTypes.head
+ protected def matchMonadSym = oneSig.finalResultType.typeSymbol
+
+ import CODE._
+ def __match(n: Name): SelectStart = matchStrategy DOT n
+
+ private lazy val oneSig: Type =
+ typer.typed(__match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message
+ }
+
+ trait PureCodegen extends CodegenCore with PureMatchMonadInterface {
+ def codegen: AbsCodegen = pureCodegen
+
+ object pureCodegen extends CommonCodegen { import CODE._
+ //// methods in MatchingStrategy (the monad companion) -- used directly in translation
+ // __match.runOrElse(`scrut`)(`scrutSym` => `matcher`)
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree
+ = __match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher))
+ // __match.one(`res`)
+ def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (__match(vpmName.one)) (res)
+ // __match.zero
+ def zero: Tree = __match(vpmName.zero)
+ // __match.guard(`c`, `then`)
+ def guard(c: Tree, then: Tree, tp: Type): Tree = __match(vpmName.guard) APPLY (c, then)
+
+ //// methods in the monad instance -- used directly in translation
+ // `prev`.flatMap(`b` => `next`)
+ def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = (prev DOT vpmName.flatMap)(fun(b, next))
+ // `thisCase`.orElse(`elseCase`)
+ def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (thisCase DOT vpmName.orElse) APPLY (elseCase)
+ // __match.guard(`cond`, `res`).flatMap(`nextBinder` => `next`)
+ def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), nextBinder, next)
+ // __match.guard(`guardTree`, ()).flatMap((_: P[Unit]) => `next`)
+ def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, pureType(UnitClass.tpe)), pureType(UnitClass.tpe), next)
+ }
+ }
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// OPTIMIZATIONS
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// decisions, decisions
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ trait TreeMakerApproximation extends TreeMakers { self: CodegenCore =>
object Test {
var currId = 0
}
@@ -957,25 +1233,16 @@ defined class Foo */
override def toString = testedPath +" (<: && ==) "+ pt +"#"+ id
}
- /** a flow-sensitive, generalised, common sub-expression elimination
- * reuse knowledge from performed tests
- * the only sub-expressions we consider are the conditions and results of the three tests (type, type&equality, equality)
- * when a sub-expression is share, it is stored in a mutable variable
- * the variable is floated up so that its scope includes all of the program that shares it
- * we generalize sharing to implication, where b reuses a if a => b and priors(a) => priors(b) (the priors of a sub expression form the path through the decision tree)
- *
- * intended to be generalised to exhaustivity/reachability checking
- */
- def doCSE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = {
+ def approximateMatch(root: Symbol, cases: List[List[TreeMaker]]): List[List[Test]] = {
// a variable in this set should never be replaced by a tree that "does not consist of a selection on a variable in this set" (intuitively)
- val pointsToBound = collection.mutable.HashSet(prevBinder)
+ val pointsToBound = collection.mutable.HashSet(root)
// the substitution that renames variables to variables in pointsToBound
var normalize: Substitution = EmptySubstitution
// replaces a variable (in pointsToBound) by a selection on another variable in pointsToBound
// TODO check:
- // pointsToBound -- accumSubst.from == Set(prevBinder) && (accumSubst.from.toSet -- pointsToBound) isEmpty
+ // pointsToBound -- accumSubst.from == Set(root) && (accumSubst.from.toSet -- pointsToBound) isEmpty
var accumSubst: Substitution = EmptySubstitution
val trees = new collection.mutable.HashSet[Tree]
@@ -1031,12 +1298,28 @@ defined class Foo */
| GuardTreeMaker(_)
| ProductExtractorTreeMaker(_, Some(_), _) => Havoc
case AlternativesTreeMaker(_, _, _) => Havoc // TODO: can do better here
- case SubstOnlyTreeMaker(_) => Top
+ case SubstOnlyTreeMaker(_, _) => Top
case BodyTreeMaker(_, _) => Havoc
}, tm)
}
- val testss = cases.map { _ map approximateTreeMaker }
+ cases.map { _ map approximateTreeMaker }
+ }
+ }
+
+////
+ trait CommonSubconditionElimination extends TreeMakerApproximation { self: OptimizedCodegen =>
+ /** a flow-sensitive, generalised, common sub-expression elimination
+ * reuse knowledge from performed tests
+ * the only sub-expressions we consider are the conditions and results of the three tests (type, type&equality, equality)
+ * when a sub-expression is share, it is stored in a mutable variable
+ * the variable is floated up so that its scope includes all of the program that shares it
+ * we generalize sharing to implication, where b reuses a if a => b and priors(a) => priors(b) (the priors of a sub expression form the path through the decision tree)
+ *
+ * intended to be generalised to exhaustivity/reachability checking
+ */
+ def doCSE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = {
+ val testss = approximateMatch(prevBinder, cases)
// interpret:
val dependencies = new collection.mutable.LinkedHashMap[Test, Set[Cond]]
@@ -1067,7 +1350,11 @@ defined class Foo */
// then, collapse these contiguous sequences of reusing tests
// store the result of the final test and the intermediate results in hoisted mutable variables (TODO: optimize: don't store intermediate results that aren't used)
// replace each reference to a variable originally bound by a collapsed test by a reference to the hoisted variable
- testss map { tests =>
+ val reused = new collection.mutable.HashMap[TreeMaker, ReusedCondTreeMaker]
+ var okToCall = false
+ val reusedOrOrig = (tm: TreeMaker) => {assert(okToCall); reused.getOrElse(tm, tm)}
+
+ val res = testss map { tests =>
var currDeps = Set[Cond]()
val (sharedPrefix, suffix) = tests span { test =>
(test.cond eq Top) || (for(
@@ -1079,18 +1366,67 @@ defined class Foo */
}
val collapsedTreeMakers = if (sharedPrefix.nonEmpty) { // even sharing prefixes of length 1 brings some benefit (overhead-percentage for compiler: 26->24%, lib: 19->16%)
- for (test <- sharedPrefix; reusedTest <- test.reuses; if reusedTest.treeMaker.isInstanceOf[FunTreeMaker])
- reusedTest.treeMaker.asInstanceOf[FunTreeMaker].reused = true
+ for (test <- sharedPrefix; reusedTest <- test.reuses) reusedTest.treeMaker match {
+ case reusedCTM: CondTreeMaker => reused(reusedCTM) = ReusedCondTreeMaker(reusedCTM)
+ case _ =>
+ }
+
// println("sharedPrefix: "+ sharedPrefix)
for (lastShared <- sharedPrefix.reverse.dropWhile(_.cond eq Top).headOption;
lastReused <- lastShared.reuses)
- yield ReusingCondTreeMaker(sharedPrefix map (t => (t.treeMaker, t.reuses map (_.treeMaker)))) :: suffix.map(_.treeMaker)
+ yield ReusingCondTreeMaker(sharedPrefix, reusedOrOrig) :: suffix.map(_.treeMaker)
} else None
collapsedTreeMakers getOrElse tests.map(_.treeMaker) // sharedPrefix need not be empty (but it only contains Top-tests, which are dropped above)
}
+ okToCall = true // TODO: remove (debugging)
+
+ res mapConserve (_ mapConserve reusedOrOrig)
+ }
+
+ object ReusedCondTreeMaker {
+ def apply(orig: CondTreeMaker) = new ReusedCondTreeMaker(orig.prevBinder, orig.nextBinder, orig.cond, orig.res, orig.pos)
+ }
+ class ReusedCondTreeMaker(prevBinder: Symbol, val nextBinder: Symbol, cond: Tree, res: Tree, pos: Position) extends TreeMaker { import CODE._
+ lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder)))
+ lazy val storedCond = freshSym(pos, BooleanClass.tpe, "rc") setFlag MUTABLE
+ lazy val treesToHoist: List[Tree] = {
+ nextBinder setFlag MUTABLE
+ List(storedCond, nextBinder) map { b => VAL(b) === codegen.mkZero(b.info) }
+ }
+
+ // TODO: finer-grained duplication
+ def chainBefore(next: Tree, pt: Type): Tree = // assert(codegen eq optimizedCodegen)
+ atPos(pos)(optimizedCodegen.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate))
+ }
+
+ case class ReusingCondTreeMaker(sharedPrefix: List[Test], toReused: TreeMaker => TreeMaker) extends TreeMaker { import CODE._
+ lazy val dropped_priors = sharedPrefix map (t => (toReused(t.treeMaker), t.reuses map (test => toReused(test.treeMaker))))
+ lazy val localSubstitution = {
+ val (from, to) = dropped_priors.collect {
+ case (dropped: CondTreeMaker, Some(prior: ReusedCondTreeMaker)) =>
+ (dropped.nextBinder, REF(prior.nextBinder))
+ }.unzip
+ val oldSubs = dropped_priors.collect {
+ case (dropped: TreeMaker, _) =>
+ dropped.substitution
+ }
+ oldSubs.foldLeft(Substitution(from, to))(_ >> _)
+ }
+
+ def chainBefore(next: Tree, pt: Type): Tree = {
+ val cond = REF(dropped_priors.reverse.collectFirst{case (_, Some(ctm: ReusedCondTreeMaker)) => ctm}.get.storedCond)
+
+ IF (cond) THEN BLOCK(
+ substitution(next).duplicate // TODO: finer-grained duplication -- MUST duplicate though, or we'll get VerifyErrors since sharing trees confuses lambdalift, and its confusion it emits illegal casts (diagnosed by Grzegorz: checkcast T ; invokevirtual S.m, where T not a subtype of S)
+ ) ELSE codegen.zero
+ }
}
+ }
+
+ //// DCE
+ trait DeadCodeElimination extends TreeMakers { self: CodegenCore =>
// TODO: non-trivial dead-code elimination
// e.g., the following match should compile to a simple instanceof:
// case class Ident(name: String)
@@ -1099,21 +1435,10 @@ defined class Foo */
// do minimal DCE
cases
}
+ }
-
- def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker])
-
- // a foldLeft to accumulate the localSubstitution left-to-right
- // it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution
- def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = {
- var accumSubst: Substitution = initial
- treeMakers foreach { maker =>
- maker incorporateOuterSubstitution accumSubst
- accumSubst = maker.substitution
- }
- removeSubstOnly(treeMakers)
- }
-
+ //// SWITCHES
+ trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface { self: CodegenCore =>
object SwitchablePattern { def unapply(pat: Tree) = pat match {
case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches
case _ => false
@@ -1128,23 +1453,35 @@ defined class Foo */
// }
// }
- def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = if (!optimizingCodeGen) None else {
+ private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe)
+
+ override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = {
def sequence[T](xs: List[Option[T]]): Option[List[T]] =
if (xs exists (_.isEmpty)) None else Some(xs.flatten)
+ def isSwitchableTpe(tpe: Type): Boolean =
+ switchableTpes contains tpe
+ def switchableConstToInt(x: Tree): Tree = {
+ val Literal(const) = x
+ const.tag match {
+ case IntTag => x
+ case ByteTag | ShortTag | CharTag => Literal(Constant(const.intValue))
+ }
+ }
+
val caseDefs = cases map { makers =>
removeSubstOnly(makers) match {
// default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches)
case (btm@BodyTreeMaker(body, _)) :: Nil =>
Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body)))
// constant
- case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => import CODE._
- Some(CaseDef(const, EmptyTree, btm.substitution(body)))
+ case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil =>
+ Some(CaseDef(switchableConstToInt(const), EmptyTree, btm.substitution(body)))
// alternatives
case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty)
val caseConstants = altss map {
case EqualityTestTreeMaker(_, const@SwitchablePattern(), _) :: Nil =>
- Some(const)
+ Some(switchableConstToInt(const))
case _ =>
None
}
@@ -1158,200 +1495,58 @@ defined class Foo */
}
}
- sequence(caseDefs) map { caseDefs =>
- import CODE._
- val matcher = BLOCK(
- VAL(scrutSym) === scrut, // TODO: type test for switchable type if patterns allow switch but the scrutinee doesn't
- Match(REF(scrutSym), caseDefs) // match on scrutSym, not scrut to avoid duplicating scrut
- )
-
- // matcher filter (tree => tree.tpe == null) foreach println
- // treeBrowser browse matcher
- matcher // set type to avoid recursion in typedMatch
- }
- }
-
- def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] =
- doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt)
-
- // calls propagateSubstitution on the treemakers
- def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){
- val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
-
- emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{
- var toHoist = List[Tree]()
- val (matcher, hasDefault) =
- if (casesUnOpt nonEmpty) {
- // when specified, need to propagate pt explicitly (type inferencer can't handle it)
- val optPt =
- if (isFullyDefined(pt)) inMatchMonad(pt)
- else NoType
-
- // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
- // exhaustivity and reachability must be checked before optimization as well
- val hasDefault = casesUnOpt.nonEmpty && {
- val nonTrivLast = casesUnOpt.last
- nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker]
+ if (!isSwitchableTpe(scrut.tpe))
+ None // TODO: emit a cast of the scrutinee and a switch on the cast scrutinee if patterns allow switch but the type of the scrutinee doesn't
+ else {
+ sequence(caseDefs) map { caseDefs =>
+ import CODE._
+ val caseDefsWithDefault = {
+ def isDefault(x: CaseDef): Boolean = x match {
+ case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true
+ case _ => false
}
-
- val cases =
- if (optimizingCodeGen) optimizeCases(scrutSym, casesUnOpt, pt)
- else casesUnOpt
-
- val combinedCases =
- cases.map(combineExtractors(_, pt)).reduceLeft(pmgen.typedOrElse(optPt))
-
- toHoist = (for (treeMakers <- cases; tm <- treeMakers; hoisted <- tm.treesToHoist) yield hoisted).toList
-
- (pmgen.fun(scrutSym, combinedCases), hasDefault)
- } else (pmgen.zero, false)
-
- val expr = pmgen.runOrElse(scrut, matcher, scrutSym.info, if (isFullyDefined(pt)) pt else NoType, hasDefault)
- if (toHoist isEmpty) expr
- else Block(toHoist, expr)
- }
- }
-
- // combineExtractors changes the current substitution's of the tree makers in `treeMakers`
- // requires propagateSubstitution(treeMakers) has been called
- def combineExtractors(treeMakers: List[TreeMaker], pt: Type): Tree =
- treeMakers.foldRight (EmptyTree: Tree) (_.chainBefore(_, pt))
-
-
-
- // TODO: do this during tree construction, but that will require tracking the current owner in treemakers
- // TODO: assign more fine-grained positions
- // fixes symbol nesting, assigns positions
- private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
- currentOwner = origOwner
-
- override def traverse(t: Tree) {
- if (t != EmptyTree && t.pos == NoPosition) {
- t.setPos(pos)
- }
- t match {
- case Function(_, _) if t.symbol == NoSymbol =>
- t.symbol = currentOwner.newAnonymousFunctionValue(t.pos)
- // println("new symbol for "+ (t, t.symbol.ownerChain))
- case Function(_, _) if (t.symbol.owner == NoSymbol) || (t.symbol.owner == origOwner) =>
- // println("fundef: "+ (t, t.symbol.ownerChain, currentOwner.ownerChain))
- t.symbol.owner = currentOwner
- case d : DefTree if (d.symbol != NoSymbol) && ((d.symbol.owner == NoSymbol) || (d.symbol.owner == origOwner)) => // don't indiscriminately change existing owners! (see e.g., pos/t3440, pos/t3534, pos/unapplyContexts2)
- // println("def: "+ (d, d.symbol.ownerChain, currentOwner.ownerChain))
- if(d.symbol.isLazy) { // for lazy val's accessor -- is there no tree??
- assert(d.symbol.lazyAccessor != NoSymbol && d.symbol.lazyAccessor.owner == d.symbol.owner, d.symbol.lazyAccessor)
- d.symbol.lazyAccessor.owner = currentOwner
+ val hasDefault = caseDefs exists isDefault
+ if (hasDefault) caseDefs else {
+ val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) }
+ caseDefs :+ default
}
- if(d.symbol.moduleClass ne NoSymbol)
- d.symbol.moduleClass.owner = currentOwner
-
- d.symbol.owner = currentOwner
- // case _ if (t.symbol != NoSymbol) && (t.symbol ne null) =>
- // println("untouched "+ (t, t.getClass, t.symbol.ownerChain, currentOwner.ownerChain))
- case _ =>
+ }
+ val matcher = BLOCK(
+ if (scrut.tpe != IntClass.tpe) {
+ scrutSym setInfo IntClass.tpe
+ VAL(scrutSym) === (scrut DOT nme.toInt)
+ } else {
+ VAL(scrutSym) === scrut
+ },
+ Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut
+ )
+ // matcher filter (tree => tree.tpe == null) foreach println
+ // treeBrowser browse matcher
+ matcher // set type to avoid recursion in typedMatch
}
- super.traverse(t)
- }
-
- // override def apply
- // println("before fixerupper: "+ xTree)
- // currentRun.trackerFactory.snapshot()
- // println("after fixerupper")
- // currentRun.trackerFactory.snapshot()
- }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// substitution
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- object Substitution {
- def apply(from: Symbol, to: Tree) = new Substitution(List(from), List(to))
- // requires sameLength(from, to)
- def apply(from: List[Symbol], to: List[Tree]) =
- if (from nonEmpty) new Substitution(from, to) else EmptySubstitution
- }
-
- class Substitution(val from: List[Symbol], val to: List[Tree]) {
- def apply(tree: Tree): Tree = typedSubst(tree, from, to)
-
- // the substitution that chains `other` before `this` substitution
- // forall t: Tree. this(other(t)) == (this >> other)(t)
- def >>(other: Substitution): Substitution = {
- val (fromFiltered, toFiltered) = (from, to).zipped filter { (f, t) => !other.from.contains(f) }
- new Substitution(other.from ++ fromFiltered, other.to.map(apply) ++ toFiltered) // a quick benchmarking run indicates the `.map(apply)` is not too costly
}
- override def toString = (from zip to) mkString("Substitution(", ", ", ")")
}
-
- object EmptySubstitution extends Substitution(Nil, Nil) {
- override def apply(tree: Tree): Tree = tree
- override def >>(other: Substitution): Substitution = other
- }
-
-
- def matchingMonadType: Type
- def typedSubst(tree: Tree, from: List[Symbol], to: List[Tree]): Tree
- def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x"): Symbol
- def typeAndEqualityTest(patBinder: Symbol, pt: Type): Tree
- def typeTest(binder: Symbol, pt: Type): Tree
-
- // codegen relevant to the structure of the translation (how extractors are combined)
- trait AbsCodeGen { import CODE.UNIT
- def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean): Tree
- def flatMap(a: Tree, b: Tree): Tree
- def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree
- def flatMapGuard(cond: Tree, next: Tree): Tree
- def fun(arg: Symbol, body: Tree): Tree
- def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree
- def zero: Tree
- def one(res: Tree, bodyPt: Type, matchPt: Type): Tree
- def condOptimized(c: Tree, then: Tree): Tree
- def _equals(checker: Tree, binder: Symbol): Tree
- def _asInstanceOf(b: Symbol, tp: Type): Tree
- def mkZero(tp: Type): Tree
- }
-
- def pmgen: AbsCodeGen
- def typed(tree: Tree, mode: Int, pt: Type): Tree // implemented in MatchTranslator
}
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// generate actual trees
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- trait MatchCodeGen extends TreeMakers {
- lazy val pmgen: CommonCodeGen with MatchingStrategyGen with MonadInstGen =
- if (optimizingCodeGen) (new CommonCodeGen with OptimizedCodeGen {})
- else (new CommonCodeGen with MatchingStrategyGen with MonadInstGen {})
-
- import CODE._
-
- trait MatchingStrategyGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen =>
- // methods in MatchingStrategy (the monad companion) -- used directly in translation
- def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean): Tree = genTypeApply(matchingStrategy DOT vpmName.runOrElse, scrutTp, resTp) APPLY (scrut) APPLY (matcher) // matchingStrategy.runOrElse(scrut)(matcher)
- // *only* used to wrap the RHS of a body (isDefinedAt synthesis relies on this)
- def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (matchingStrategy DOT vpmName.one) (_asInstanceOf(res, bodyPt, force = true)) // matchingStrategy.one(res), like one, but blow this one away for isDefinedAt (since it's the RHS of a case)
- def zero: Tree = matchingStrategy DOT vpmName.zero // matchingStrategy.zero
- def guard(c: Tree, then: Tree, tp: Type): Tree = genTypeApply((matchingStrategy DOT vpmName.guard), repackExistential(tp)) APPLY (c, then) // matchingStrategy.guard[tp](c, then)
- }
+ trait OptimizedMatchMonadInterface extends MatchMonadInterface {
+ override def inMatchMonad(tp: Type): Type = optionType(tp)
+ override def pureType(tp: Type): Type = tp
+ override protected def matchMonadSym = OptionClass
+ }
- trait MonadInstGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen =>
- // methods in the monad instance -- used directly in translation
- def flatMap(a: Tree, b: Tree): Tree = (a DOT vpmName.flatMap)(b)
- def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (genTypeApply(thisCase DOT vpmName.orElse, pt)) APPLY (elseCase)
+ trait OptimizedCodegen extends CodegenCore with TypedSubstitution with OptimizedMatchMonadInterface {
+ override def codegen: AbsCodegen = optimizedCodegen
- // TODO: the trees generated by flatMapCond and flatMapGuard may need to be distinguishable by exhaustivity checking -- they aren't right now
- def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol,
- nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), fun(nextBinder, next))
- def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, UnitClass.tpe), UnitClass.tpe, next)
- }
+ // trait AbsOptimizedCodegen extends AbsCodegen {
+ // def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree
+ // }
+ // def optimizedCodegen: AbsOptimizedCodegen
// when we know we're targetting Option, do some inlining the optimizer won't do
- // `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard
- // this is a special instance of the advanced inlining optimization that takes a method call on
- // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases
- // this trait overrides ALL of the methods of MatchingStrategyGen with MonadInstGen
- trait OptimizedCodeGen extends CommonCodeGen with MatchingStrategyGen with MonadInstGen {
+ // for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard
+ // this is a special instance of the advanced inlining optimization that takes a method call on
+ // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases
+ object optimizedCodegen extends CommonCodegen /*with AbsOptimizedCodegen*/ { import CODE._
lazy val zeroSym = freshSym(NoPosition, optionType(NothingClass.tpe), "zero")
/** Inline runOrElse and get rid of Option allocations
@@ -1363,23 +1558,22 @@ defined class Foo */
@inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass)
lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE
lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE
- override def runOrElse(scrut: Tree, matcher: Tree, scrutTp: Type, resTp: Type, hasDefault: Boolean) = {
- val Function(List(x: ValDef), body) = matcher
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = {
matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL
if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245
BLOCK(
- VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite...
- VAL(x.symbol) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for x.symbol in body -- the owner structure is repaired by fixerUpper
+ VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite...
+ VAL(scrutSym) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for scrutSym in `matcher` -- the owner structure is repaired by fixerUpper
VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above
VAL(keepGoing) === TRUE,
- body,
+ matcher,
if(hasDefault) REF(matchRes)
- else (IF (REF(keepGoing)) THEN MATCHERROR(REF(x.symbol)) ELSE REF(matchRes))
+ else (IF (REF(keepGoing)) THEN MATCHERROR(REF(scrutSym)) ELSE REF(matchRes))
)
}
// only used to wrap the RHS of a body
- override def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = {
+ def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = {
BLOCK(
if (dontStore(matchPt)) res // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse...
else (REF(matchRes) === res), // _asInstanceOf(res, tp.widen, force = true)
@@ -1388,194 +1582,57 @@ defined class Foo */
)
}
- override def zero: Tree = REF(zeroSym)
-
- // guard is only used by flatMapCond and flatMapGuard, which are overridden
- override def guard(c: Tree, then: Tree, tp: Type): Tree = throw new NotImplementedError("guard is never called by optimizing codegen")
+ def zero: Tree = REF(zeroSym)
- override def flatMap(opt: Tree, fun: Tree): Tree = fun match {
- case Function(List(x: ValDef), body) =>
- val tp = inMatchMonad(x.symbol.tpe)
- val vs = freshSym(opt.pos, tp, "o")
- val isEmpty = tp member vpmName.isEmpty
- val get = tp member vpmName.get
- val v = VAL(vs) === opt
+ def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = {
+ val tp = inMatchMonad(b.tpe)
+ val prevSym = freshSym(prev.pos, tp, "o")
+ val isEmpty = tp member vpmName.isEmpty
+ val get = tp member vpmName.get
- BLOCK(
- v,
- IF (vs DOT isEmpty) THEN zero ELSE typedSubst(body, List(x.symbol), List(vs DOT get)) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor)
- )
- case _ => println("huh?")
- (opt DOT vpmName.flatMap)(fun)
+ BLOCK(
+ VAL(prevSym) === prev,
+ IF (prevSym DOT isEmpty) THEN zero ELSE Substitution(b, prevSym DOT get)(next) // must be isEmpty and get as we don't control the target of the call (could be the result of a user-defined extractor)
+ )
}
- override def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = {
+ def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = {
BLOCK(
thisCase,
IF (REF(keepGoing)) THEN elseCase ELSE zero // leave trailing zero for now, otherwise typer adds () anyway
)
}
- override def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree =
+ def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree =
IF (cond) THEN BLOCK(
VAL(nextBinder) === res,
next
) ELSE zero
- override def flatMapGuard(guardTree: Tree, next: Tree): Tree =
- IF (guardTree) THEN next ELSE zero
- }
-
- @inline private def typedIfOrigTyped(to: Tree, origTp: Type): Tree =
- if (origTp == null || origTp == NoType) to
- // important: only type when actually substing and when original tree was typed
- // (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors)
- else typed(to, EXPRmode, WildcardType)
-
- // We must explicitly type the trees that we replace inside some other tree, since the latter may already have been typed,
- // and will thus not be retyped. This means we might end up with untyped subtrees inside bigger, typed trees.
- def typedSubst(tree: Tree, from: List[Symbol], to: List[Tree]): Tree = {
- // according to -Ystatistics 10% of translateMatch's time is spent in this method...
- // since about half of the typedSubst's end up being no-ops, the check below shaves off 5% of the time spent in typedSubst
- if (!tree.exists { case i@Ident(_) => from contains i.symbol case _ => false}) tree
- else (new Transformer {
- override def transform(tree: Tree): Tree = {
- def subst(from: List[Symbol], to: List[Tree]): Tree =
- if (from.isEmpty) tree
- else if (tree.symbol == from.head) typedIfOrigTyped(to.head.shallowDuplicate, tree.tpe)
- else subst(from.tail, to.tail)
-
- tree match {
- case Ident(_) => subst(from, to)
- case _ => super.transform(tree)
- }
- }
- }).transform(tree)
- }
-
- var ctr = 0
- def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = {ctr += 1;
- // assert(owner ne null)
- // assert(owner ne NoSymbol)
- NoSymbol.newTermSymbol(vpmName.counted(prefix, ctr), pos) setInfo repackExistential(tp)
- }
-
- def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
- case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args)
- case _ => tp
- }
+ def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree =
+ IF (cond) THEN BLOCK(
+ condSym === TRUE,
+ nextBinder === res,
+ next
+ ) ELSE zero
- object vpmName {
- val one = newTermName("one")
- val drop = newTermName("drop")
- val flatMap = newTermName("flatMap")
- val get = newTermName("get")
- val guard = newTermName("guard")
- val isEmpty = newTermName("isEmpty")
- val orElse = newTermName("orElse")
- val outer = newTermName("<outer>")
- val runOrElse = newTermName("runOrElse")
- val zero = newTermName("zero")
-
- def counted(str: String, i: Int) = newTermName(str+i)
+ def flatMapGuard(guardTree: Tree, next: Tree): Tree =
+ IF (guardTree) THEN next ELSE zero
}
+ }
- def typesConform(tp: Type, pt: Type) = ((tp eq pt) || (tp <:< pt))
-
- trait CommonCodeGen extends AbsCodeGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen =>
- def fun(arg: Symbol, body: Tree): Tree = Function(List(ValDef(arg)), body)
- def genTypeApply(tfun: Tree, args: Type*): Tree = if(args contains NoType) tfun else TypeApply(tfun, args.toList map TypeTree)
- def tupleSel(binder: Symbol)(i: Int): Tree = (REF(binder) DOT nme.productAccessorName(i)) // make tree that accesses the i'th component of the tuple referenced by binder
- def index(tgt: Tree)(i: Int): Tree = tgt APPLY (LIT(i))
- def drop(tgt: Tree)(n: Int): Tree = (tgt DOT vpmName.drop) (LIT(n))
- def _equals(checker: Tree, binder: Symbol): Tree = checker MEMBER_== REF(binder) // NOTE: checker must be the target of the ==, that's the patmat semantics for ya
- def and(a: Tree, b: Tree): Tree = a AND b
- def condOptimized(c: Tree, then: Tree): Tree = IF (c) THEN then ELSE zero
-
- // the force is needed mainly to deal with the GADT typing hack (we can't detect it otherwise as tp nor pt need contain an abstract type, we're just casting wildly)
- def _asInstanceOf(t: Tree, tp: Type, force: Boolean = false): Tree = { val tpX = repackExistential(tp)
- if (!force && (t.tpe ne NoType) && t.isTyped && typesConform(t.tpe, tpX)) t //{ println("warning: emitted redundant asInstanceOf: "+(t, t.tpe, tp)); t } //.setType(tpX)
- else gen.mkAsInstanceOf(t, tpX, true, false)
- }
-
- def _isInstanceOf(b: Symbol, tp: Type): Tree = gen.mkIsInstanceOf(REF(b), repackExistential(tp), true, false)
- // { val tpX = repackExistential(tp)
- // if (typesConform(b.info, tpX)) { println("warning: emitted spurious isInstanceOf: "+(b, tp)); TRUE }
- // else gen.mkIsInstanceOf(REF(b), tpX, true, false)
- // }
-
- def _asInstanceOf(b: Symbol, tp: Type): Tree = { val tpX = repackExistential(tp)
- if (typesConform(b.info, tpX)) REF(b) //{ println("warning: emitted redundant asInstanceOf: "+(b, b.info, tp)); REF(b) } //.setType(tpX)
- else gen.mkAsInstanceOf(REF(b), tpX, true, false)
- }
-
- // duplicated out of frustration with cast generation
- def mkZero(tp: Type): Tree = {
- tp.typeSymbol match {
- case UnitClass => Literal(Constant())
- case BooleanClass => Literal(Constant(false))
- case FloatClass => Literal(Constant(0.0f))
- case DoubleClass => Literal(Constant(0.0d))
- case ByteClass => Literal(Constant(0.toByte))
- case ShortClass => Literal(Constant(0.toShort))
- case IntClass => Literal(Constant(0))
- case LongClass => Literal(Constant(0L))
- case CharClass => Literal(Constant(0.toChar))
- case _ => gen.mkAsInstanceOf(Literal(Constant(null)), tp, any = true, wrapInApply = false) // the magic incantation is true/false here
- }
- }
+ trait MatchOptimizations extends CommonSubconditionElimination
+ with DeadCodeElimination
+ with SwitchEmission
+ with OptimizedCodegen { self: TreeMakers =>
+ override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): (List[List[TreeMaker]], List[Tree]) = {
+ val optCases = doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt)
+ val toHoist = (
+ for (treeMakers <- optCases)
+ yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist}
+ ).flatten.flatten.toList
+ (optCases, toHoist)
}
-
- def matchingStrategy: Tree
}
}
-
-// object noShadowedUntyped extends Traverser {
-// override def traverse(t: Tree) {
-// if ((t.tpe ne null) && (t.tpe ne NoType)) okTree = t
-// else if(okTree ne null) println("untyped subtree "+ t +" in typed tree"+ okTree +" : "+ okTree.tpe)
-// super.traverse(t)
-// }
-// var okTree: Tree = null
-// }
-// private def c(t: Tree): Tree = noShadowedUntyped(t)
-
- // def approximateTreeMaker(tm: TreeMaker): List[Test] = tm match {
- // case ExtractorTreeMaker(extractor, _, _) => HavocTest
- // case FilteredExtractorTreeMaker(extractor, lenGuard, _, _) => HavocTest
- // case ProductExtractorTreeMaker(testedBinder, lenGuard, _) => TopTest // TODO: (testedBinder ne null) and lenGuard
- //
- // // cond = typeTest(prevBinder, nextBinderTp)
- // // res = pmgen._asInstanceOf(prevBinder, nextBinderTp)
- // case TypeTestTreeMaker(testedBinder, pt, _) =>
- //
- // // cond = typeAndEqualityTest(patBinder, pt)
- // // res = pmgen._asInstanceOf(patBinder, nextBinderTp)
- // case TypeAndEqualityTestTreeMaker(_, testedBinder, pt, _) =>
- //
- // // cond = pmgen._equals(patTree, prevBinder)
- // // res = CODE.REF(prevBinder)
- // case EqualityTestTreeMaker(testedBinder, rhs, _) =>
- //
- // case AlternativesTreeMaker(_, alts: *) =>
- //
- // case GuardTreeMaker(guardTree) =>
- // }
-
- // // TODO: it's not exactly sound to represent an unapply-call by its symbol... also need to consider the prefix, like the outer-test (can this be captured as the path to this test?)
- // type ExtractorRepr = Symbol
- //
- // // TODO: we're undoing tree-construction that we ourselves performed earlier -- how about not-doing so we don't have to undo?
- // private def findBinderArgOfApply(extractor: Tree, unappSym: Symbol): Symbol = {
- // class CollectTreeTraverser[T](pf: PartialFunction[Tree => T]) extends Traverser {
- // val hits = new ListBuffer[T]
- // override def traverse(t: Tree) {
- // if (pf.isDefinedAt(t)) hits += pf(t)
- // super.traverse(t)
- // }
- // }
- // val trav = new CollectTreeTraverser{ case Apply(unapp, List(arg)) if unapp.symbol eq unappSym => arg.symbol}
- // trav.traverse(extractor)
- // trav.hits.headOption getOrElse NoSymbol
- // }
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index f7a6815905..376488d4ef 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -926,9 +926,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
def validateVarianceArgs(tps: List[Type], variance: Int, tparams: List[Symbol]) {
- (tps zip tparams) foreach {
- case (tp, tparam) => validateVariance(tp, variance * tparam.variance)
- }
+ foreach2(tps, tparams)((tp, tparam) => validateVariance(tp, variance * tparam.variance))
}
validateVariance(base.info, CoVariance)
@@ -1036,10 +1034,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
/** Symbols which limit the warnings we can issue since they may be value types */
val isMaybeValue = Set(AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass)
- // Whether def equals(other: Any) is overridden
- def isUsingDefaultEquals = {
+ // Whether def equals(other: Any) is overridden or synthetic
+ def isUsingWarnableEquals = {
val m = receiver.info.member(nme.equals_)
- (m == Object_equals) || (m == Any_equals)
+ (m == Object_equals) || (m == Any_equals) || (m.isSynthetic && m.owner.isCase)
}
// Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere.
def isUsingDefaultScalaOp = {
@@ -1047,7 +1045,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
(s == Object_==) || (s == Object_!=) || (s == Any_==) || (s == Any_!=)
}
// Whether the operands+operator represent a warnable combo (assuming anyrefs)
- def isWarnable = isReferenceOp || (isUsingDefaultEquals && isUsingDefaultScalaOp)
+ // Looking for comparisons performed with ==/!= in combination with either an
+ // equals method inherited from Object or a case class synthetic equals (for
+ // which we know the logic.)
+ def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals)
def isEitherNullable = (NullClass.tpe <:< receiver.info) || (NullClass.tpe <:< actual.info)
def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass
def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 680a298230..5f555f6697 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -100,6 +100,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case MethodType(params, _) =>
val argResultsBuff = new ListBuffer[SearchResult]()
val argBuff = new ListBuffer[Tree]()
+ var paramFailed = false
def mkPositionalArg(argTree: Tree, paramName: Name) = argTree
def mkNamedArg(argTree: Tree, paramName: Name) = atPos(argTree.pos)(new AssignOrNamedArg(Ident(paramName), (argTree)))
@@ -114,14 +115,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
for(ar <- argResultsBuff)
paramTp = paramTp.subst(ar.subst.from, ar.subst.to)
- val res = inferImplicit(fun, paramTp, true, false, context)
+ val res = if (paramFailed) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, false, context)
argResultsBuff += res
if (res != SearchFailure) {
argBuff += mkArg(res.tree, param.name)
} else {
mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args
- if (!param.hasDefault) {
+ if (!param.hasDefault && !paramFailed) {
context.errBuffer.find(_.kind == ErrorKinds.Divergent) match {
case Some(divergentImplicit) =>
// DivergentImplicit error has higher priority than "no implicit found"
@@ -133,6 +134,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case None =>
NoImplicitFoundError(fun, param)
}
+ paramFailed = true
}
/* else {
TODO: alternative (to expose implicit search failure more) -->
@@ -767,7 +769,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree)){ typer1 =>
if (original != EmptyTree && pt != WildcardType)
- typer1.silent(tpr => tpr.typed(tpr.applyImplicitArgs(tree), mode, pt)) match {
+ typer1.silent(tpr => {
+ val withImplicitArgs = tpr.applyImplicitArgs(tree)
+ if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway
+ else tpr.typed(withImplicitArgs, mode, pt)
+ }) match {
case SilentResultValue(result) =>
result
case _ =>
@@ -2920,7 +2926,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
existentialAbstraction(captured.toList, tpe)
}
- /** convert skolems to existentials */
+ /** convert local symbols and skolems to existentials */
def packedType(tree: Tree, owner: Symbol): Type = {
def defines(tree: Tree, sym: Symbol) =
sym.isExistentialSkolem && sym.unpackLocation == tree ||
@@ -3302,7 +3308,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val owntype = elimAnonymousClass(owntype0)
if (needAdapt) cases1 = cases1 map (adaptCase(_, owntype))
- (new MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match {
+ (MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match {
case Block(vd :: Nil, tree@Match(selector, cases)) =>
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt)
diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java
index 8b0338ed29..d4c5417260 100644
--- a/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java
+++ b/src/fjbg/ch/epfl/lamp/fjbg/JExtendedCode.java
@@ -596,6 +596,16 @@ public class JExtendedCode extends JCode {
double minDensity) {
assert keys.length == branches.length;
+ //The special case for empty keys. It makes sense to allow
+ //empty keys and generate LOOKUPSWITCH with defaultBranch
+ //only. This is exactly what javac does for switch statement
+ //that has only a default case.
+ if (keys.length == 0) {
+ emitLOOKUPSWITCH(keys, branches, defaultBranch);
+ return;
+ }
+ //the rest of the code assumes that keys.length > 0
+
// sorting the tables
// FIXME use quicksort
for (int i = 1; i < keys.length; i++) {
diff --git a/src/library/scala/MatchingStrategy.scala b/src/library/scala/MatchingStrategy.scala
deleted file mode 100644
index d11598bad6..0000000000
--- a/src/library/scala/MatchingStrategy.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-package scala
-
-abstract class MatchingStrategy[M[+x]] {
- // runs the matcher on the given input
- def runOrElse[T, U](in: T)(matcher: T => M[U]): U
-
- def zero: M[Nothing]
- def one[T](x: T): M[T]
- def guard[T](cond: Boolean, then: => T): M[T]
- def isSuccess[T, U](x: T)(f: T => M[U]): Boolean // used for isDefinedAt
-
- def caseResult[T](x: T): M[T] = one(x) // used as a marker to distinguish the RHS of a case (case pat => RHS) and intermediate successes
- // when deriving a partial function from a pattern match,
- // we need to distinguish the RHS of a case, which should not be evaluated when computing isDefinedAt,
- // from an intermediate result (which must be computed)
-}
-
-object MatchingStrategy {
- implicit object OptionMatchingStrategy extends MatchingStrategy[Option] {
- type M[+x] = Option[x]
- @inline def runOrElse[T, U](x: T)(f: T => M[U]): U = f(x) getOrElse (throw new MatchError(x))
- @inline def zero: M[Nothing] = None
- @inline def one[T](x: T): M[T] = Some(x)
- @inline def guard[T](cond: Boolean, then: => T): M[T] = if(cond) Some(then) else None
- @inline def isSuccess[T, U](x: T)(f: T => M[U]): Boolean = !f(x).isEmpty
- }
-} \ No newline at end of file
diff --git a/src/library/scala/collection/IndexedSeqOptimized.scala b/src/library/scala/collection/IndexedSeqOptimized.scala
index e2541f2a66..196e77c91b 100755
--- a/src/library/scala/collection/IndexedSeqOptimized.scala
+++ b/src/library/scala/collection/IndexedSeqOptimized.scala
@@ -104,7 +104,7 @@ trait IndexedSeqOptimized[+A, +Repr] extends IndexedSeqLike[A, Repr] { self =>
override /*IterableLike*/
def slice(from: Int, until: Int): Repr = {
val lo = math.max(from, 0)
- val hi = math.min(until, length)
+ val hi = math.min(math.max(until, 0), length)
val elems = math.max(hi - lo, 0)
val b = newBuilder
b.sizeHint(elems)
diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala
index 497de92c80..c1f45eccfb 100644
--- a/src/library/scala/math/BigDecimal.scala
+++ b/src/library/scala/math/BigDecimal.scala
@@ -33,8 +33,10 @@ object BigDecimal {
/** Cache ony for defaultMathContext using BigDecimals in a small range. */
private lazy val cache = new Array[BigDecimal](maxCached - minCached + 1)
- object RoundingMode extends Enumeration(java.math.RoundingMode.values map (_.toString) : _*) with Serializable {
+ object RoundingMode extends Enumeration {
type RoundingMode = Value
+ // These are supposed to be the same as java.math.RoundingMode.values,
+ // though it seems unwise to rely on the correspondence.
val UP, DOWN, CEILING, FLOOR, HALF_UP, HALF_DOWN, HALF_EVEN, UNNECESSARY = Value
}
diff --git a/src/library/scala/reflect/api/StandardDefinitions.scala b/src/library/scala/reflect/api/StandardDefinitions.scala
index 08071660a2..3526cf259d 100755
--- a/src/library/scala/reflect/api/StandardDefinitions.scala
+++ b/src/library/scala/reflect/api/StandardDefinitions.scala
@@ -12,7 +12,7 @@ trait StandardDefinitions { self: Universe =>
abstract class AbsDefinitions {
// outer packages and their classes
- def RootPackage: Symbol
+ def RootPackage: Symbol // under consideration
def RootClass: Symbol
def EmptyPackage: Symbol
def EmptyPackageClass: Symbol
@@ -46,6 +46,11 @@ trait StandardDefinitions { self: Universe =>
def StringClass : Symbol
def ClassClass : Symbol
+ // product, tuple, function
+ def TupleClass : Array[Symbol]
+ def ProductClass : Array[Symbol]
+ def FunctionClass : Array[Symbol]
+
// fundamental modules
def PredefModule: Symbol
diff --git a/src/library/scala/reflect/api/Symbols.scala b/src/library/scala/reflect/api/Symbols.scala
index 01c1a0f2ae..17d9b06324 100755
--- a/src/library/scala/reflect/api/Symbols.scala
+++ b/src/library/scala/reflect/api/Symbols.scala
@@ -79,7 +79,7 @@ trait Symbols { self: Universe =>
/** A list of annotations attached to this Symbol.
*/
- def annotations: List[self.AnnotationInfo]
+ def getAnnotations: List[self.AnnotationInfo]
/** For a class: the module or case class factory with the same name in the same package.
* For all others: NoSymbol
diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala
index 03b043c188..0a38fb45bf 100644
--- a/src/library/scala/reflect/api/Trees.scala
+++ b/src/library/scala/reflect/api/Trees.scala
@@ -537,6 +537,9 @@ trait Trees { self: Universe =>
// The symbol of a This is the class to which the this refers.
// For instance in C.this, it would be C.
+ def This(sym: Symbol): Tree =
+ This(sym.name.toTypeName) setSymbol sym
+
/** Designator <qualifier> . <name> */
case class Select(qualifier: Tree, name: Name)
extends RefTree
diff --git a/src/partest/scala/tools/partest/utils/CodeTest.scala b/src/partest/scala/tools/partest/utils/CodeTest.scala
index c90168a313..c236d89bbd 100644
--- a/src/partest/scala/tools/partest/utils/CodeTest.scala
+++ b/src/partest/scala/tools/partest/utils/CodeTest.scala
@@ -24,11 +24,17 @@ object CodeTest {
def apply[T](code: Code[T], args: Array[String] = Array()) = {
println("testing: "+code.tree)
+ println("type is: "+code.manifest.tpe)
+ val isNullary = code.manifest.tpe.typeSymbol == scala.reflect.mirror.definitions.FunctionClass(0)
val reporter = new ConsoleReporter(new Settings)
val toolbox = new ToolBox(reporter, args mkString " ")
val ttree = toolbox.typeCheck(code.tree, code.manifest.tpe)
- println("result = " + toolbox.showAttributed(ttree))
- val evaluated = toolbox.runExpr(ttree)
+ println("result = " + toolbox.showAttributed(ttree, printTypes = true, printIds = false))
+ var evaluated = toolbox.runExpr(ttree)
+ if (evaluated != null && isNullary) {
+ val applyMeth = evaluated.getClass.getMethod("apply")
+ evaluated = applyMeth.invoke(evaluated)
+ }
println("evaluated = "+evaluated)
evaluated
}