From 2df3934168f62d8662d5f29c1b0db6119353406d Mon Sep 17 00:00:00 2001 From: "Daniel C. Sobral" Date: Thu, 26 Jan 2012 18:07:49 -0200 Subject: Performance improvements for CommentFactory. A few small improvements to the parsing steps of CommentFactory (both main parsing and wiki format parsing), resulting in a 30% reduction of time spent in def parse, which gives me 5% reduction in docs.lib. Turn docBody into a StringBuilder, to improve concatenation speed. Make WikiParser and CharReader work with String instead of Array[String], to reduce copying of data. Get rid of the implicit conversion from String to Array[String]. Also, adjust a couple of regex to avoid expensive backtracking. The new regex has a different semantics, which is arguably more correct than the former one. There's absolutely no change in the generated docs for Scala anyway. --- .../nsc/doc/model/comment/CommentFactory.scala | 100 +++++++++++---------- 1 file changed, 53 insertions(+), 47 deletions(-) (limited to 'src/compiler') 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]*)?>\u000E))(.*)""") + new Regex("""(.*?)((?:\{\{\{)|(?:\u000E]*)?>\u000E))(.*)""") /** The end of a scaladoc code block */ protected val CodeBlockEnd = - new Regex("""(.*)((?:\}\}\})|(?:\u000E\u000E))(.*)""") + new Regex("""(.*?)((?:\}\}\})|(?:\u000E\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() -- cgit v1.2.3 From 2e664079445549288789ad24a95ce7d583ae205c Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 31 Jan 2012 11:30:41 +0100 Subject: Introduce getAnnotations that triggers symbol completion Default getter for annotations doesn't perform initialization, hence we've faced the following bug: https://issues.scala-lang.org/browse/SI-5423. One of the approaches to fixing it would be to auto-complete on getter, but according to Martin we'd better not do that because of cycles. That's why I'm just introducing a new, eager, variation of `annotations' and redirecting public API to it. Review by @odersky. --- src/compiler/scala/reflect/internal/Symbols.scala | 10 ++++++++++ src/library/scala/reflect/api/Symbols.scala | 2 +- test/files/run/t5423.check | 1 + test/files/run/t5423.scala | 12 ++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t5423.check create mode 100644 test/files/run/t5423.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 94d764067f..e777491300 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1272,6 +1272,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/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/test/files/run/t5423.check b/test/files/run/t5423.check new file mode 100644 index 0000000000..ae3d3fb82b --- /dev/null +++ b/test/files/run/t5423.check @@ -0,0 +1 @@ +List(table) \ No newline at end of file diff --git a/test/files/run/t5423.scala b/test/files/run/t5423.scala new file mode 100644 index 0000000000..2139773ff1 --- /dev/null +++ b/test/files/run/t5423.scala @@ -0,0 +1,12 @@ +import java.lang.Class +import scala.reflect.mirror._ +import scala.reflect.runtime.Mirror.ToolBox +import scala.reflect.Code + +final class table extends StaticAnnotation +@table class A + +object Test extends App{ + val s = classToSymbol(classOf[A]) + println(s.getAnnotations) +} -- cgit v1.2.3 From 147e9eaf3814738f339b020e701a160ba2f68b60 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 31 Jan 2012 08:21:34 -0800 Subject: Improved warning for insensible comparisons. Utilize knowledge of case class synthetic equals to rule out some comparisons statically. Closes SI-5426. --- src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 11 +++++++---- test/files/neg/checksensible.check | 5 ++++- test/files/neg/t5426.check | 13 +++++++++++++ test/files/neg/t5426.flags | 1 + test/files/neg/t5426.scala | 10 ++++++++++ 5 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 test/files/neg/t5426.check create mode 100644 test/files/neg/t5426.flags create mode 100644 test/files/neg/t5426.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index a99d09173e..a6c2f75d5e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1034,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 = { @@ -1045,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/test/files/neg/checksensible.check b/test/files/neg/checksensible.check index 0881205bb4..d785179a56 100644 --- a/test/files/neg/checksensible.check +++ b/test/files/neg/checksensible.check @@ -28,6 +28,9 @@ checksensible.scala:27: error: comparing values of types Int and Unit using `==' checksensible.scala:29: error: comparing values of types Int and String using `==' will always yield false 1 == "abc" ^ +checksensible.scala:33: error: comparing values of types Some[Int] and Int using `==' will always yield false + Some(1) == 1 // as above + ^ checksensible.scala:38: error: comparing a fresh object using `==' will always yield false new AnyRef == 1 ^ @@ -94,4 +97,4 @@ checksensible.scala:84: error: comparing values of types EqEqRefTest.this.C3 and checksensible.scala:95: error: comparing values of types Unit and Int using `!=' will always yield true while ((c = in.read) != -1) ^ -32 errors found +33 errors found diff --git a/test/files/neg/t5426.check b/test/files/neg/t5426.check new file mode 100644 index 0000000000..d9e192d3f0 --- /dev/null +++ b/test/files/neg/t5426.check @@ -0,0 +1,13 @@ +t5426.scala:2: error: comparing values of types Some[Int] and Int using `==' will always yield false + def f1 = Some(5) == 5 + ^ +t5426.scala:3: error: comparing values of types Int and Some[Int] using `==' will always yield false + def f2 = 5 == Some(5) + ^ +t5426.scala:8: error: comparing values of types Int and Some[Int] using `==' will always yield false + (x1 == x2) + ^ +t5426.scala:9: error: comparing values of types Some[Int] and Int using `==' will always yield false + (x2 == x1) + ^ +four errors found diff --git a/test/files/neg/t5426.flags b/test/files/neg/t5426.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t5426.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/neg/t5426.scala b/test/files/neg/t5426.scala new file mode 100644 index 0000000000..f2fb5cc12c --- /dev/null +++ b/test/files/neg/t5426.scala @@ -0,0 +1,10 @@ +class A { + def f1 = Some(5) == 5 + def f2 = 5 == Some(5) + + val x1 = 5 + val x2 = Some(5) + + (x1 == x2) + (x2 == x1) +} -- cgit v1.2.3 From e311585e26449a921bc8a40b87b2552f1d363086 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 25 Jan 2012 18:38:38 +0100 Subject: Unfold pattern alternatives in genicode. Implemented unfolding of pattern alternatives that can be translated into switch table in genicode. This way pattern matcher can keep simple patterns as-is and let backend handle translation of them instead of generating bunch of LabelDefs and jumps. Review by @dragos or @magarciaEPFL as both seem to know genicode very well. --- src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/compiler') 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)) -- cgit v1.2.3 From c0e87de8e9421cc2e03a066f307206f967fe518a Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 1 Feb 2012 07:52:55 -0800 Subject: Tweak to repl debugging output. --- src/compiler/scala/tools/nsc/interpreter/IMain.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 567d6c2f78..56bb72ca6f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -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 -- cgit v1.2.3 From 97912733f9e7e2c2528ebbab6b70ef35b8dd0fbc Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 25 Jan 2012 18:42:24 +0100 Subject: Get rid of unused import. --- src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 44579400ff..0b35f1b1d0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1138,7 +1138,7 @@ defined class Foo */ 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._ + case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => Some(CaseDef(const, EmptyTree, btm.substitution(body))) // alternatives case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty) -- cgit v1.2.3 From aa7759651d25ab8c315a2d36e3f28cf3caaa041f Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 25 Jan 2012 18:42:52 +0100 Subject: Generate default case for switches. --- .../scala/tools/nsc/typechecker/PatMatVirtualiser.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 0b35f1b1d0..cf5985eeee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1160,9 +1160,20 @@ defined class Foo */ 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 hasDefault = caseDefs exists isDefault + if (hasDefault) caseDefs else { + val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } + caseDefs :+ default + } + } 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 + Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut ) // matcher filter (tree => tree.tpe == null) foreach println -- cgit v1.2.3 From 39457f6c85fc9764d714d52317edcd4300fd82b8 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Wed, 1 Feb 2012 17:26:11 +0100 Subject: Convert values to Int in switchable patterns. Further improvements to how -Yvirtpatmat handles switch-like patterns that can be translated to switch tables. First of all, we added a check whether a type of an expression we pattern match on is in the set of allowed types for switch patterns. If yes, we translate a pattern to switch one by converting both an expression we pattern match on and literals in a pattern to an Int. I borrowed an idea of converting to Ints from both old pattern matcher implementation and from how javac handles it. --- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 62 ++++++++++++++-------- 1 file changed, 41 insertions(+), 21 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index cf5985eeee..b1e02cb062 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -1128,10 +1128,22 @@ defined class Foo */ // } // } + private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) + def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = if (!optimizingCodeGen) None else { 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) @@ -1139,12 +1151,12 @@ defined class Foo */ Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body))) // constant case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil => - Some(CaseDef(const, EmptyTree, btm.substitution(body))) + 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,27 +1170,35 @@ defined class Foo */ } } - 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 hasDefault = caseDefs exists isDefault - if (hasDefault) caseDefs else { - val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } - caseDefs :+ default + if (!isSwitchableTpe(scrut.tpe)) + None + 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 hasDefault = caseDefs exists isDefault + if (hasDefault) caseDefs else { + val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) } + caseDefs :+ default + } } + 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 } - val matcher = BLOCK( - VAL(scrutSym) === scrut, // TODO: type test for switchable type if patterns allow switch but the scrutinee doesn't - 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 } } -- cgit v1.2.3 From b2a21c4eacdddb0ee59a8c74c8a73e6cc34cb6bc Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 1 Feb 2012 10:21:22 -0800 Subject: Added getPackage to the repl classloader. --- .../nsc/interpreter/AbstractFileClassLoader.scala | 4 ++++ .../scala/tools/nsc/interpreter/IMain.scala | 26 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala index 3bc4e1cbe1..70fa740eeb 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -20,6 +20,10 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) with ScalaClassLoader { // private val defined = mutable.Map[String, Class[_]]() + + // Widening to public + override def getPackage(name: String) = super.getPackage(name) + override protected def trace = sys.props contains "scala.debug.classloader" diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 56bb72ca6f..4ccea8afd6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -314,6 +314,26 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(virtualDirectory, parent) { private[IMain] var traceClassLoading = isReplTrace override protected def trace = super.trace || traceClassLoading + + private val packages = mutable.HashMap[String, Package]() + private def enclosingPackageNames(name: String): List[String] = + (name split '.').inits.toList drop 1 dropRight 1 map (_ mkString ".") reverse + + // Here's what all those params to definePackage are after the package name: + // + // specTitle - The specification title + // specVersion - The specification version + // specVendor - The specification vendor + // implTitle - The implementation title + // implVersion - The implementation version + // implVendor - The implementation vendor + // sealBase - If not null, then this package is sealed with respect to the given code source URL object. Otherwise, the package is not sealed. + private def addPackageNames(name: String) { + enclosingPackageNames(name) filterNot (packages contains _) foreach { p => + packages(p) = definePackage(p, "", "", "", "", "", "", null) + repltrace("Added " + packages(p) + " to repl classloader.") + } + } /** Overridden here to try translating a simple name to the generated * class name if the original attempt fails. This method is used by @@ -328,6 +348,12 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends file } } + override def findClass(name: String): JClass = { + val clazz = super.findClass(name) + if (clazz ne null) + addPackageNames(clazz.getName) + clazz + } } private def makeClassLoader(): AbstractFileClassLoader = new TranslatingClassLoader(parentClassLoader match { -- cgit v1.2.3 From 264ff5d5e8dbec4ae2e13bf52e66a965d884b25c Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 31 Jan 2012 00:14:47 -0800 Subject: Fix for parser OOM. The scanner performs some sketchy heuristics when it sees an ascii 1A since it may be EOF or it may be part of a literal. Due to this, it failed to detect an unterminated string literal if the opening quote was unicode-escaped, leading to memory exhaustion as it read SUs until the universe ended. We're parsing a fixed input with known length! There's no reason to be guessing about whether a char is EOF. If we're at the end of the file, it's the end of file. Otherwise, it is not the end of the file. --- src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 10 ++++++---- test/files/neg/unicode-unterminated-quote.check | 4 ++++ test/files/neg/unicode-unterminated-quote.scala | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 test/files/neg/unicode-unterminated-quote.check create mode 100644 test/files/neg/unicode-unterminated-quote.scala (limited to 'src/compiler') 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/test/files/neg/unicode-unterminated-quote.check b/test/files/neg/unicode-unterminated-quote.check new file mode 100644 index 0000000000..fc5caa6d7e --- /dev/null +++ b/test/files/neg/unicode-unterminated-quote.check @@ -0,0 +1,4 @@ +unicode-unterminated-quote.scala:2: error: unclosed string literal + val x = /u0022 + ^ +one error found diff --git a/test/files/neg/unicode-unterminated-quote.scala b/test/files/neg/unicode-unterminated-quote.scala new file mode 100644 index 0000000000..bb6eab667f --- /dev/null +++ b/test/files/neg/unicode-unterminated-quote.scala @@ -0,0 +1,2 @@ +class A { + val x = \u0022 \ No newline at end of file -- cgit v1.2.3 From 68218fa678abfce9cce9f764e3cb9463ce6e0b85 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 1 Feb 2012 14:05:25 -0800 Subject: Update RoundingMode. Not to use the deprecated Enumeration constructor. --- src/compiler/scala/tools/nsc/io/Path.scala | 2 +- src/library/scala/math/BigDecimal.scala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/compiler') 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/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 } -- cgit v1.2.3 From 1e0707786b118e3e33379e7acdc75306b45e6547 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 31 Jan 2012 12:12:23 +0100 Subject: Hardens classToType logic Reflection now correctly processes classes, objects and inner classes that are declared in classes and objects. However classToType still crashes on compound types and local classes. For more information on those, follow the links: * Compound types: https://issues.scala-lang.org/browse/SI-5430 * Local classes: https://issues.scala-lang.org/browse/SI-5431 Fixes https://issues.scala-lang.org/browse/SI-5256. Review by @paulp, @odersky. --- .../scala/reflect/runtime/JavaToScala.scala | 118 +++++++++++++++------ .../nsc/interpreter/AbstractFileClassLoader.scala | 39 ++++++- .../scala/tools/nsc/interpreter/IMain.scala | 30 +----- test/files/run/t5256a.check | 2 + test/files/run/t5256a.scala | 9 ++ test/files/run/t5256b.check | 2 + test/files/run/t5256b.scala | 8 ++ test/files/run/t5256d.check | 20 ++++ test/files/run/t5256d.scala | 10 ++ test/files/run/t5256e.check | 2 + test/files/run/t5256e.scala | 9 ++ test/files/run/t5256f.check | 4 + test/files/run/t5256f.scala | 19 ++++ test/pending/run/t5256c.check | 0 test/pending/run/t5256c.scala | 10 ++ test/pending/run/t5256g.check | 0 test/pending/run/t5256g.scala | 11 ++ test/pending/run/t5256h.check | 8 ++ test/pending/run/t5256h.scala | 8 ++ 19 files changed, 247 insertions(+), 62 deletions(-) create mode 100644 test/files/run/t5256a.check create mode 100644 test/files/run/t5256a.scala create mode 100644 test/files/run/t5256b.check create mode 100644 test/files/run/t5256b.scala create mode 100644 test/files/run/t5256d.check create mode 100644 test/files/run/t5256d.scala create mode 100644 test/files/run/t5256e.check create mode 100644 test/files/run/t5256e.scala create mode 100644 test/files/run/t5256f.check create mode 100644 test/files/run/t5256f.scala create mode 100644 test/pending/run/t5256c.check create mode 100644 test/pending/run/t5256c.scala create mode 100644 test/pending/run/t5256g.check create mode 100644 test/pending/run/t5256g.scala create mode 100644 test/pending/run/t5256h.check create mode 100644 test/pending/run/t5256h.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/runtime/JavaToScala.scala b/src/compiler/scala/reflect/runtime/JavaToScala.scala index b4bcc52a23..4c49c0221f 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/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala index 70fa740eeb..3a605975f4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -21,9 +21,6 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) { // private val defined = mutable.Map[String, Class[_]]() - // Widening to public - override def getPackage(name: String) = super.getPackage(name) - override protected def trace = sys.props contains "scala.debug.classloader" @@ -47,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 @@ -78,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 4ccea8afd6..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 @@ -314,26 +314,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(virtualDirectory, parent) { private[IMain] var traceClassLoading = isReplTrace override protected def trace = super.trace || traceClassLoading - - private val packages = mutable.HashMap[String, Package]() - private def enclosingPackageNames(name: String): List[String] = - (name split '.').inits.toList drop 1 dropRight 1 map (_ mkString ".") reverse - - // Here's what all those params to definePackage are after the package name: - // - // specTitle - The specification title - // specVersion - The specification version - // specVendor - The specification vendor - // implTitle - The implementation title - // implVersion - The implementation version - // implVendor - The implementation vendor - // sealBase - If not null, then this package is sealed with respect to the given code source URL object. Otherwise, the package is not sealed. - private def addPackageNames(name: String) { - enclosingPackageNames(name) filterNot (packages contains _) foreach { p => - packages(p) = definePackage(p, "", "", "", "", "", "", null) - repltrace("Added " + packages(p) + " to repl classloader.") - } - } /** Overridden here to try translating a simple name to the generated * class name if the original attempt fails. This method is used by @@ -348,12 +328,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends file } } - override def findClass(name: String): JClass = { - val clazz = super.findClass(name) - if (clazz ne null) - addPackageNames(clazz.getName) - clazz - } } private def makeClassLoader(): AbstractFileClassLoader = new TranslatingClassLoader(parentClassLoader match { @@ -1104,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/test/files/run/t5256a.check b/test/files/run/t5256a.check new file mode 100644 index 0000000000..304f4ddd79 --- /dev/null +++ b/test/files/run/t5256a.check @@ -0,0 +1,2 @@ +A +true diff --git a/test/files/run/t5256a.scala b/test/files/run/t5256a.scala new file mode 100644 index 0000000000..05a935c770 --- /dev/null +++ b/test/files/run/t5256a.scala @@ -0,0 +1,9 @@ +import scala.reflect.mirror._ + +class A + +object Test extends App { + val c = classToType(classOf[A]) + println(c) + println(c.typeSymbol == classToSymbol(classOf[A])) +} diff --git a/test/files/run/t5256b.check b/test/files/run/t5256b.check new file mode 100644 index 0000000000..64f4c01166 --- /dev/null +++ b/test/files/run/t5256b.check @@ -0,0 +1,2 @@ +Test.A +true \ No newline at end of file diff --git a/test/files/run/t5256b.scala b/test/files/run/t5256b.scala new file mode 100644 index 0000000000..5575211641 --- /dev/null +++ b/test/files/run/t5256b.scala @@ -0,0 +1,8 @@ +import scala.reflect.mirror._ + +object Test extends App { + class A + val c = classToType(classOf[A]) + println(c) + println(c.typeSymbol == classToSymbol(classOf[A])) +} diff --git a/test/files/run/t5256d.check b/test/files/run/t5256d.check new file mode 100644 index 0000000000..7924c15c5c --- /dev/null +++ b/test/files/run/t5256d.check @@ -0,0 +1,20 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> + +scala> import scala.reflect.mirror._ +import scala.reflect.mirror._ + +scala> class A +defined class A + +scala> val c = classToType(classOf[A]) +c: reflect.mirror.Type = A + +scala> println(c.typeSymbol == classToSymbol(classOf[A])) +true + +scala> + +scala> diff --git a/test/files/run/t5256d.scala b/test/files/run/t5256d.scala new file mode 100644 index 0000000000..86404a9b63 --- /dev/null +++ b/test/files/run/t5256d.scala @@ -0,0 +1,10 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ +import scala.reflect.mirror._ +class A +val c = classToType(classOf[A]) +println(c.typeSymbol == classToSymbol(classOf[A])) + """ +} diff --git a/test/files/run/t5256e.check b/test/files/run/t5256e.check new file mode 100644 index 0000000000..e50f917e14 --- /dev/null +++ b/test/files/run/t5256e.check @@ -0,0 +1,2 @@ +C.this.A +true \ No newline at end of file diff --git a/test/files/run/t5256e.scala b/test/files/run/t5256e.scala new file mode 100644 index 0000000000..9ed422ca44 --- /dev/null +++ b/test/files/run/t5256e.scala @@ -0,0 +1,9 @@ +import scala.reflect.mirror._ + +class C { class A } + +object Test extends App { + val c = classToType(classOf[C#A]) + println(c) + println(c.typeSymbol == classToSymbol(classOf[C#A])) +} diff --git a/test/files/run/t5256f.check b/test/files/run/t5256f.check new file mode 100644 index 0000000000..ad2f375d9a --- /dev/null +++ b/test/files/run/t5256f.check @@ -0,0 +1,4 @@ +Test.A1 +true +Test.this.A2 +true diff --git a/test/files/run/t5256f.scala b/test/files/run/t5256f.scala new file mode 100644 index 0000000000..45c80cbd63 --- /dev/null +++ b/test/files/run/t5256f.scala @@ -0,0 +1,19 @@ +import scala.reflect.mirror._ + +object Test extends App { + class A1 + + val c1 = classToType(classOf[A1]) + println(c1) + println(c1.typeSymbol == classToSymbol(classOf[A1])) + + new Test +} + +class Test { + class A2 + + val c2 = classToType(classOf[A2]) + println(c2) + println(c2.typeSymbol == classToSymbol(classOf[A2])) +} diff --git a/test/pending/run/t5256c.check b/test/pending/run/t5256c.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/pending/run/t5256c.scala b/test/pending/run/t5256c.scala new file mode 100644 index 0000000000..8ebb51a009 --- /dev/null +++ b/test/pending/run/t5256c.scala @@ -0,0 +1,10 @@ +import scala.reflect.mirror._ + +object Test extends App { + { + class A + val c = classToType(classOf[A]) + println(c) + println(c.typeSymbol == classToSymbol(classOf[A])) + } +} diff --git a/test/pending/run/t5256g.check b/test/pending/run/t5256g.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/pending/run/t5256g.scala b/test/pending/run/t5256g.scala new file mode 100644 index 0000000000..6158a9281d --- /dev/null +++ b/test/pending/run/t5256g.scala @@ -0,0 +1,11 @@ +import scala.reflect.mirror._ + +class A +trait B + +object Test extends App { + val mutant = new A with B + val c = classToType(mutant.getClass) + println(c) + println(c.typeSymbol == classToSymbol(mutant.getClass)) +} diff --git a/test/pending/run/t5256h.check b/test/pending/run/t5256h.check new file mode 100644 index 0000000000..4f9b8faf71 --- /dev/null +++ b/test/pending/run/t5256h.check @@ -0,0 +1,8 @@ +import scala.reflect.mirror._ + +object Test extends App { + val mutant = new { val x = 2 } + val c = classToType(mutant.getClass) + println(c) + println(c.typeSymbol == classToSymbol(mutant.getClass)) +} diff --git a/test/pending/run/t5256h.scala b/test/pending/run/t5256h.scala new file mode 100644 index 0000000000..4f9b8faf71 --- /dev/null +++ b/test/pending/run/t5256h.scala @@ -0,0 +1,8 @@ +import scala.reflect.mirror._ + +object Test extends App { + val mutant = new { val x = 2 } + val c = classToType(mutant.getClass) + println(c) + println(c.typeSymbol == classToSymbol(mutant.getClass)) +} -- cgit v1.2.3 From 610027b3c50c6a46b26bcfe71013cebc172c146b Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 31 Jan 2012 15:02:48 +0100 Subject: Hardens reification against rare kinds of Constants Importers now correctly process constants that carry types and symbols. However, it is still impossible to reify classOf for a class/trait that is defined inside a quasiquote. Theoretically, this can be implemented, but will require attaching original trees to classOf constants, which needs much more effort. --- .../scala/reflect/internal/Constants.scala | 2 +- .../scala/reflect/internal/Importers.scala | 17 ++++++++----- .../scala/tools/nsc/transform/LiftCode.scala | 28 +++++++++++++++++++--- test/files/run/t5258a.check | 1 + test/files/run/t5258a.scala | 14 +++++++++++ test/pending/run/t5258b.check | 1 + test/pending/run/t5258b.scala | 15 ++++++++++++ test/pending/run/t5258c.check | 1 + test/pending/run/t5258c.scala | 15 ++++++++++++ 9 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 test/files/run/t5258a.check create mode 100644 test/files/run/t5258a.scala create mode 100644 test/pending/run/t5258b.check create mode 100644 test/pending/run/t5258b.scala create mode 100644 test/pending/run/t5258c.check create mode 100644 test/pending/run/t5258c.scala (limited to 'src/compiler') 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/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala index 23b443919a..4f5b28d370 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) => @@ -339,5 +339,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/tools/nsc/transform/LiftCode.scala b/src/compiler/scala/tools/nsc/transform/LiftCode.scala index c5475fa0f2..f1182fc2a9 100644 --- a/src/compiler/scala/tools/nsc/transform/LiftCode.scala +++ b/src/compiler/scala/tools/nsc/transform/LiftCode.scala @@ -129,7 +129,13 @@ abstract class LiftCode extends Transform with TypingTransformers { if (reifyCopypaste) printCopypaste(result) result } - } finally printTypings = saved + } catch { + case ex: ReifierError => + unit.error(ex.pos, ex.msg) + tree + } finally { + printTypings = saved + } case _ => super.transform(tree) } @@ -396,6 +402,10 @@ abstract class LiftCode extends Transform with TypingTransformers { if (thereAreOnlyTTs && ttsAreNotEssential) 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) boundSyms += tree.symbol @@ -494,8 +504,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/test/files/run/t5258a.check b/test/files/run/t5258a.check new file mode 100644 index 0000000000..4e0b2da04c --- /dev/null +++ b/test/files/run/t5258a.check @@ -0,0 +1 @@ +int \ No newline at end of file diff --git a/test/files/run/t5258a.scala b/test/files/run/t5258a.scala new file mode 100644 index 0000000000..deabb8310f --- /dev/null +++ b/test/files/run/t5258a.scala @@ -0,0 +1,14 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + println(classOf[Int]) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} \ No newline at end of file diff --git a/test/pending/run/t5258b.check b/test/pending/run/t5258b.check new file mode 100644 index 0000000000..283b4225fb --- /dev/null +++ b/test/pending/run/t5258b.check @@ -0,0 +1 @@ +TBI \ No newline at end of file diff --git a/test/pending/run/t5258b.scala b/test/pending/run/t5258b.scala new file mode 100644 index 0000000000..70cb4a7f4e --- /dev/null +++ b/test/pending/run/t5258b.scala @@ -0,0 +1,15 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + class C + println(classOf[C]) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} \ No newline at end of file diff --git a/test/pending/run/t5258c.check b/test/pending/run/t5258c.check new file mode 100644 index 0000000000..283b4225fb --- /dev/null +++ b/test/pending/run/t5258c.check @@ -0,0 +1 @@ +TBI \ No newline at end of file diff --git a/test/pending/run/t5258c.scala b/test/pending/run/t5258c.scala new file mode 100644 index 0000000000..a93170d0d6 --- /dev/null +++ b/test/pending/run/t5258c.scala @@ -0,0 +1,15 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + object E extends Enumeration { val foo, bar = Value } + println(E.foo) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} \ No newline at end of file -- cgit v1.2.3 From 4a083558bd20a46b0b29ed0b5b5ec7a0a1f29888 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 2 Feb 2012 12:06:34 +0100 Subject: Fix sbt build with trunk. This was tricky to find as HLists and multiple chains of implicits are definitely not fun to debug. Reporting ambiguous errors is influenced by the general error reporting, don't look for implicit arguments if any of the preceding ones failed (kills performance, causes diverging implicits with HLists). Previously throwing type errors handled that correctly but now we don't do that. Fixed small but essential typo when typing implicit. Review by @dragos --- src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src/compiler') 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/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d3ff331f98..a90067a56c 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 _ => -- cgit v1.2.3 From d940371bd50098c4146e52941880ccdbcb4ea47a Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 2 Feb 2012 14:39:44 +0100 Subject: Miscellaneous fixes to reification More specifically: * Importers now preserve wasEmpty and original * ToolBoxes no longer auto-evaluate nullary functions returned by runExpr * All local symbols from previous typechecks are now correctly erased by ResetAttrs * Originals are now reified --- .../scala/reflect/internal/Importers.scala | 15 ++- src/compiler/scala/reflect/runtime/ToolBoxes.scala | 49 ++++++---- src/compiler/scala/tools/nsc/ast/Trees.scala | 107 ++++++++++++--------- .../scala/tools/nsc/settings/ScalaSettings.scala | 4 +- .../scala/tools/nsc/transform/LiftCode.scala | 62 +++++++++--- .../scala/tools/nsc/typechecker/Typers.scala | 2 +- .../scala/reflect/api/StandardDefinitions.scala | 7 +- .../scala/tools/partest/utils/CodeTest.scala | 10 +- test/files/run/code.check | 7 ++ test/files/run/reify_complex.check | 1 + test/files/run/reify_complex.scala | 31 ++++++ test/files/run/reify_extendbuiltins.check | 1 + test/files/run/reify_extendbuiltins.scala | 21 ++++ test/files/run/reify_generic2.check | 1 + test/files/run/reify_generic2.scala | 16 +++ test/files/run/reify_getter.check | 1 + test/files/run/reify_getter.scala | 19 ++++ test/files/run/reify_sort1.check | 2 + test/files/run/reify_sort1.scala | 27 ++++++ test/files/run/t5269.check | 1 + test/files/run/t5269.scala | 22 +++++ test/files/run/t5274_1.check | 3 + test/files/run/t5274_1.scala | 20 ++++ test/files/run/t5275.check | 1 + test/files/run/t5275.scala | 15 +++ test/files/run/t5277_1.check | 1 + test/files/run/t5277_1.scala | 21 ++++ test/files/run/t5277_2.check | 2 + test/files/run/t5277_2.scala | 18 ++++ test/files/run/t5335.check | 1 + test/files/run/t5335.scala | 14 +++ test/pending/run/reify_closure6.check | 6 +- test/pending/run/reify_closure6.scala | 2 + test/pending/run/reify_closure7.check | 6 ++ test/pending/run/reify_closure7.scala | 32 ++++++ test/pending/run/reify_closure8a.check | 1 + test/pending/run/reify_closure8a.scala | 17 ++++ test/pending/run/reify_closure8b.check | 1 + test/pending/run/reify_closure8b.scala | 17 ++++ test/pending/run/reify_closure9a.check | 1 + test/pending/run/reify_closure9a.scala | 20 ++++ test/pending/run/reify_closure9b.check | 1 + test/pending/run/reify_closure9b.scala | 20 ++++ test/pending/run/reify_closures10.check | 2 + test/pending/run/reify_closures10.scala | 15 +++ test/pending/run/reify_closures11.check | 1 + test/pending/run/reify_closures11.scala | 18 ++++ test/pending/run/reify_complex.check | 1 - test/pending/run/reify_complex.scala | 31 ------ test/pending/run/reify_extendbuiltins.check | 1 - test/pending/run/reify_extendbuiltins.scala | 21 ---- test/pending/run/reify_sort1.check | 2 - test/pending/run/reify_sort1.scala | 27 ------ test/pending/run/reify_this.check | 5 + test/pending/run/reify_this.scala | 31 ++++++ test/pending/run/t5269.check | 1 - test/pending/run/t5269.scala | 22 ----- test/pending/run/t5274_1.check | 3 - test/pending/run/t5274_1.scala | 20 ---- test/pending/run/t5275.check | 1 - test/pending/run/t5275.scala | 15 --- test/pending/run/t5277_1.check | 1 - test/pending/run/t5277_1.scala | 21 ---- test/pending/run/t5277_2.check | 2 - test/pending/run/t5277_2.scala | 18 ---- test/pending/run/t5415.check | 0 test/pending/run/t5415.scala | 14 +++ 67 files changed, 625 insertions(+), 274 deletions(-) create mode 100644 test/files/run/reify_complex.check create mode 100644 test/files/run/reify_complex.scala create mode 100644 test/files/run/reify_extendbuiltins.check create mode 100644 test/files/run/reify_extendbuiltins.scala create mode 100644 test/files/run/reify_generic2.check create mode 100644 test/files/run/reify_generic2.scala create mode 100644 test/files/run/reify_getter.check create mode 100644 test/files/run/reify_getter.scala create mode 100644 test/files/run/reify_sort1.check create mode 100644 test/files/run/reify_sort1.scala create mode 100644 test/files/run/t5269.check create mode 100644 test/files/run/t5269.scala create mode 100644 test/files/run/t5274_1.check create mode 100644 test/files/run/t5274_1.scala create mode 100644 test/files/run/t5275.check create mode 100644 test/files/run/t5275.scala create mode 100644 test/files/run/t5277_1.check create mode 100644 test/files/run/t5277_1.scala create mode 100644 test/files/run/t5277_2.check create mode 100644 test/files/run/t5277_2.scala create mode 100644 test/files/run/t5335.check create mode 100644 test/files/run/t5335.scala create mode 100644 test/pending/run/reify_closure7.check create mode 100644 test/pending/run/reify_closure7.scala create mode 100644 test/pending/run/reify_closure8a.check create mode 100644 test/pending/run/reify_closure8a.scala create mode 100644 test/pending/run/reify_closure8b.check create mode 100644 test/pending/run/reify_closure8b.scala create mode 100644 test/pending/run/reify_closure9a.check create mode 100644 test/pending/run/reify_closure9a.scala create mode 100644 test/pending/run/reify_closure9b.check create mode 100644 test/pending/run/reify_closure9b.scala create mode 100644 test/pending/run/reify_closures10.check create mode 100644 test/pending/run/reify_closures10.scala create mode 100644 test/pending/run/reify_closures11.check create mode 100644 test/pending/run/reify_closures11.scala delete mode 100644 test/pending/run/reify_complex.check delete mode 100644 test/pending/run/reify_complex.scala delete mode 100644 test/pending/run/reify_extendbuiltins.check delete mode 100644 test/pending/run/reify_extendbuiltins.scala delete mode 100644 test/pending/run/reify_sort1.check delete mode 100644 test/pending/run/reify_sort1.scala create mode 100644 test/pending/run/reify_this.check create mode 100644 test/pending/run/reify_this.scala delete mode 100644 test/pending/run/t5269.check delete mode 100644 test/pending/run/t5269.scala delete mode 100644 test/pending/run/t5274_1.check delete mode 100644 test/pending/run/t5274_1.scala delete mode 100644 test/pending/run/t5275.check delete mode 100644 test/pending/run/t5275.scala delete mode 100644 test/pending/run/t5277_1.check delete mode 100644 test/pending/run/t5277_1.scala delete mode 100644 test/pending/run/t5277_2.check delete mode 100644 test/pending/run/t5277_2.scala create mode 100644 test/pending/run/t5415.check create mode 100644 test/pending/run/t5415.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala index 4f5b28d370..6c843e6f15 100644 --- a/src/compiler/scala/reflect/internal/Importers.scala +++ b/src/compiler/scala/reflect/internal/Importers.scala @@ -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 } 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(""), 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/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/transform/LiftCode.scala b/src/compiler/scala/tools/nsc/transform/LiftCode.scala index f1182fc2a9..197a52f011 100644 --- a/src/compiler/scala/tools/nsc/transform/LiftCode.scala +++ b/src/compiler/scala/tools/nsc/transform/LiftCode.scala @@ -55,10 +55,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._ @@ -123,11 +129,14 @@ 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 } } catch { case ex: ReifierError => @@ -145,7 +154,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))), @@ -274,6 +284,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) @@ -300,7 +318,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 { @@ -381,6 +399,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 => @@ -393,13 +419,21 @@ 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 _) => @@ -407,8 +441,10 @@ abstract class LiftCode extends Transform with TypingTransformers { 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) /* diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d3ff331f98..4cf134d58b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2915,7 +2915,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 || 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/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 } diff --git a/test/files/run/code.check b/test/files/run/code.check index b946554fda..9b0351bbf9 100644 --- a/test/files/run/code.check +++ b/test/files/run/code.check @@ -1,29 +1,36 @@ testing: ((x: Int) => x.$plus(ys.length)) +type is: Int => Int result = ((x: Int) => x.+{(x: )Int}(ys.length{Int}){Int}){Int => Int} evaluated = testing: (() => { val e: Element = new Element("someName"); e }) +type is: () => Element result = (() => { val e: Element = new Element{Element}{(name: )Element}("someName"{String("someName")}){Element}; e{Element} }{Element}){() => Element} evaluated = Element(someName) testing: (() => truc.elem = 6) +type is: () => Unit result = (() => truc.elem{Int} = 6{Int(6)}{Unit}){() => Unit} evaluated = null testing: (() => truc.elem = truc.elem.$plus(6)) +type is: () => Unit result = (() => truc.elem{Int} = truc.elem.+{(x: )Int}(6{Int(6)}){Int}{Unit}){() => Unit} evaluated = null testing: (() => new baz.BazElement("someName")) +type is: () => baz.BazElement result = (() => new baz.BazElement{baz.BazElement}{(name: )baz.BazElement}("someName"{String("someName")}){baz.BazElement}){() => baz.BazElement} evaluated = BazElement(someName) testing: ((x: Int) => x.$plus(ys.length)) +type is: Int => Int result = ((x: Int) => x.+{(x: )Int}(ys.length{Int}){Int}){Int => Int} evaluated = static: 2 testing: (() => x.$plus(1)) +type is: () => Int result = (() => x.+{(x: )Int}(1{Int(1)}){Int}){() => Int} evaluated = 2 1+1 = 2 diff --git a/test/files/run/reify_complex.check b/test/files/run/reify_complex.check new file mode 100644 index 0000000000..7df35e33a0 --- /dev/null +++ b/test/files/run/reify_complex.check @@ -0,0 +1 @@ +3.0+4.0*i diff --git a/test/files/run/reify_complex.scala b/test/files/run/reify_complex.scala new file mode 100644 index 0000000000..aae4d558cf --- /dev/null +++ b/test/files/run/reify_complex.scala @@ -0,0 +1,31 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + class Complex(val re: Double, val im: Double) { + def + (that: Complex) = + new Complex(re + that.re, im + that.im) + def - (that: Complex) = + new Complex(re - that.re, im - that.im) + def * (that: Complex) = + new Complex(re * that.re - im * that.im, + re * that.im + im * that.re) + def / (that: Complex) = { + val denom = that.re * that.re + that.im * that.im + new Complex((re * that.re + im * that.im) / denom, + (im * that.re - re * that.im) / denom) + } + override def toString = + re + (if (im < 0) "-" + (-im) else "+" + im) + "*i" + } + val x = new Complex(2, 1); val y = new Complex(1, 3) + println(x + y) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/reify_extendbuiltins.check b/test/files/run/reify_extendbuiltins.check new file mode 100644 index 0000000000..a48033a30d --- /dev/null +++ b/test/files/run/reify_extendbuiltins.check @@ -0,0 +1 @@ +10! = 3628800 diff --git a/test/files/run/reify_extendbuiltins.scala b/test/files/run/reify_extendbuiltins.scala new file mode 100644 index 0000000000..57acd699ff --- /dev/null +++ b/test/files/run/reify_extendbuiltins.scala @@ -0,0 +1,21 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + def fact(n: Int): BigInt = + if (n == 0) 1 else fact(n-1) * n + class Factorizer(n: Int) { + def ! = fact(n) + } + implicit def int2fact(n: Int) = new Factorizer(n) + + println("10! = " + (10!)) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/reify_generic2.check b/test/files/run/reify_generic2.check new file mode 100644 index 0000000000..b8626c4cff --- /dev/null +++ b/test/files/run/reify_generic2.check @@ -0,0 +1 @@ +4 diff --git a/test/files/run/reify_generic2.scala b/test/files/run/reify_generic2.scala new file mode 100644 index 0000000000..d03fe7602b --- /dev/null +++ b/test/files/run/reify_generic2.scala @@ -0,0 +1,16 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + class C + val product = List(new C, new C).length * List[C](new C, new C).length + println(product) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/reify_getter.check b/test/files/run/reify_getter.check new file mode 100644 index 0000000000..5ef4ff4d04 --- /dev/null +++ b/test/files/run/reify_getter.check @@ -0,0 +1 @@ +evaluated = 2 diff --git a/test/files/run/reify_getter.scala b/test/files/run/reify_getter.scala new file mode 100644 index 0000000000..83eaded506 --- /dev/null +++ b/test/files/run/reify_getter.scala @@ -0,0 +1,19 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + class C { + val x = 2 + } + + new C().x + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + val evaluated = toolbox.runExpr(ttree) + println("evaluated = " + evaluated) +} diff --git a/test/files/run/reify_sort1.check b/test/files/run/reify_sort1.check new file mode 100644 index 0000000000..0d30805141 --- /dev/null +++ b/test/files/run/reify_sort1.check @@ -0,0 +1,2 @@ +List(6, 2, 8, 5, 1) +List(1, 2, 5, 6, 8) diff --git a/test/files/run/reify_sort1.scala b/test/files/run/reify_sort1.scala new file mode 100644 index 0000000000..42f4c824a5 --- /dev/null +++ b/test/files/run/reify_sort1.scala @@ -0,0 +1,27 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + def sort(a: List[Int]): List[Int] = { + if (a.length < 2) + a + else { + val pivot = a(a.length / 2) + sort(a.filter(_ < pivot)) ::: + a.filter(_ == pivot) ::: + sort(a.filter(_ > pivot)) + } + } + + val xs = List(6, 2, 8, 5, 1) + println(xs) + println(sort(xs)) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5269.check b/test/files/run/t5269.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t5269.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t5269.scala b/test/files/run/t5269.scala new file mode 100644 index 0000000000..a30509f3fe --- /dev/null +++ b/test/files/run/t5269.scala @@ -0,0 +1,22 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + trait Z { + val z = 2 + } + + class X extends Z { + def println() = Predef.println(z) + } + + new X().println() + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5274_1.check b/test/files/run/t5274_1.check new file mode 100644 index 0000000000..fca8bc3d3e --- /dev/null +++ b/test/files/run/t5274_1.check @@ -0,0 +1,3 @@ +50! = 30414093201713378043612608166064768844377641568960512000000000000 +49! = 608281864034267560872252163321295376887552831379210240000000000 +50!/49! = 50 diff --git a/test/files/run/t5274_1.scala b/test/files/run/t5274_1.scala new file mode 100644 index 0000000000..c501172518 --- /dev/null +++ b/test/files/run/t5274_1.scala @@ -0,0 +1,20 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + def factorial(n: BigInt): BigInt = + if (n == 0) 1 else n * factorial(n-1) + + val f50 = factorial(50); val f49 = factorial(49) + println("50! = " + f50) + println("49! = " + f49) + println("50!/49! = " + (f50 / f49)) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5275.check b/test/files/run/t5275.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t5275.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t5275.scala b/test/files/run/t5275.scala new file mode 100644 index 0000000000..d419834ded --- /dev/null +++ b/test/files/run/t5275.scala @@ -0,0 +1,15 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + class C(val foo: Int) + println(new C(2).foo) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5277_1.check b/test/files/run/t5277_1.check new file mode 100644 index 0000000000..a48033a30d --- /dev/null +++ b/test/files/run/t5277_1.check @@ -0,0 +1 @@ +10! = 3628800 diff --git a/test/files/run/t5277_1.scala b/test/files/run/t5277_1.scala new file mode 100644 index 0000000000..57acd699ff --- /dev/null +++ b/test/files/run/t5277_1.scala @@ -0,0 +1,21 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + def fact(n: Int): BigInt = + if (n == 0) 1 else fact(n-1) * n + class Factorizer(n: Int) { + def ! = fact(n) + } + implicit def int2fact(n: Int) = new Factorizer(n) + + println("10! = " + (10!)) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5277_2.check b/test/files/run/t5277_2.check new file mode 100644 index 0000000000..ca017e2a40 --- /dev/null +++ b/test/files/run/t5277_2.check @@ -0,0 +1,2 @@ +2() +1() diff --git a/test/files/run/t5277_2.scala b/test/files/run/t5277_2.scala new file mode 100644 index 0000000000..67b6b000bc --- /dev/null +++ b/test/files/run/t5277_2.scala @@ -0,0 +1,18 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + def p(implicit i: Int) = print(i) + implicit val v = 2 + + println(p) + println(p(1)) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5335.check b/test/files/run/t5335.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t5335.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t5335.scala b/test/files/run/t5335.scala new file mode 100644 index 0000000000..9a8b91f04d --- /dev/null +++ b/test/files/run/t5335.scala @@ -0,0 +1,14 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + println(new {def x = 2}.x) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/pending/run/reify_closure6.check b/test/pending/run/reify_closure6.check index 3526d04b0e..e521ea874d 100644 --- a/test/pending/run/reify_closure6.check +++ b/test/pending/run/reify_closure6.check @@ -1,3 +1,7 @@ +q = 1 +y = 1 first invocation = 15 -second invocation = 18 +q = 2 +y = 1 +second invocation = 17 q after second invocation = 2 diff --git a/test/pending/run/reify_closure6.scala b/test/pending/run/reify_closure6.scala index 909071aa44..43ddfde28d 100644 --- a/test/pending/run/reify_closure6.scala +++ b/test/pending/run/reify_closure6.scala @@ -10,6 +10,8 @@ object Test extends App { val fun: reflect.Code[Int => Int] = x => { y += 1 q += 1 + println("q = " + q) + println("y = " + y) x + ys.length * z + q + y } diff --git a/test/pending/run/reify_closure7.check b/test/pending/run/reify_closure7.check new file mode 100644 index 0000000000..bf58b52bce --- /dev/null +++ b/test/pending/run/reify_closure7.check @@ -0,0 +1,6 @@ +q = 1 +y = 1 +first invocation = 15 +q = 2 +y = 2 +second invocation = 17 diff --git a/test/pending/run/reify_closure7.scala b/test/pending/run/reify_closure7.scala new file mode 100644 index 0000000000..8933df23fa --- /dev/null +++ b/test/pending/run/reify_closure7.scala @@ -0,0 +1,32 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + var q = 0 + var clo: Int => Int = null + def foo[T](ys: List[T]): Int => Int = { + val z = 1 + var y = 0 + val fun: reflect.Code[Int => Int] = x => { + y += 1 + q += 1 + println("q = " + q) + println("y = " + y) + x + ys.length * z + q + y + } + + if (clo == null) { + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + clo = dyn.asInstanceOf[Int => Int] + } + + clo + } + + println("first invocation = " + foo(List(1, 2, 3))(10)) + println("second invocation = " + foo(List(1, 2, 3, 4))(10)) +} diff --git a/test/pending/run/reify_closure8a.check b/test/pending/run/reify_closure8a.check new file mode 100644 index 0000000000..9a037142aa --- /dev/null +++ b/test/pending/run/reify_closure8a.check @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/test/pending/run/reify_closure8a.scala b/test/pending/run/reify_closure8a.scala new file mode 100644 index 0000000000..5e54bfc8c7 --- /dev/null +++ b/test/pending/run/reify_closure8a.scala @@ -0,0 +1,17 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + class Foo(val y: Int) { + def fun = lift{y} + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(new Foo(10).fun.tree) + val dyn = toolbox.runExpr(ttree) + val foo = dyn.asInstanceOf[Int] + println(foo) +} diff --git a/test/pending/run/reify_closure8b.check b/test/pending/run/reify_closure8b.check new file mode 100644 index 0000000000..9a037142aa --- /dev/null +++ b/test/pending/run/reify_closure8b.check @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/test/pending/run/reify_closure8b.scala b/test/pending/run/reify_closure8b.scala new file mode 100644 index 0000000000..9e37e4e09a --- /dev/null +++ b/test/pending/run/reify_closure8b.scala @@ -0,0 +1,17 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + class Foo(y: Int) { + def fun = lift{y} + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(new Foo(10).fun.tree) + val dyn = toolbox.runExpr(ttree) + val foo = dyn.asInstanceOf[Int] + println(foo) +} diff --git a/test/pending/run/reify_closure9a.check b/test/pending/run/reify_closure9a.check new file mode 100644 index 0000000000..9a037142aa --- /dev/null +++ b/test/pending/run/reify_closure9a.check @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/test/pending/run/reify_closure9a.scala b/test/pending/run/reify_closure9a.scala new file mode 100644 index 0000000000..f3ee153d3c --- /dev/null +++ b/test/pending/run/reify_closure9a.scala @@ -0,0 +1,20 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def foo(y: Int) = { + class Foo(val y: Int) { + def fun = lift{y} + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(new Foo(y).fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int] + } + + println(foo(10)) +} diff --git a/test/pending/run/reify_closure9b.check b/test/pending/run/reify_closure9b.check new file mode 100644 index 0000000000..9a037142aa --- /dev/null +++ b/test/pending/run/reify_closure9b.check @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/test/pending/run/reify_closure9b.scala b/test/pending/run/reify_closure9b.scala new file mode 100644 index 0000000000..8d349e8701 --- /dev/null +++ b/test/pending/run/reify_closure9b.scala @@ -0,0 +1,20 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def foo(y: Int) = { + class Foo(y: Int) { + def fun = lift{y} + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(new Foo(y).fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int] + } + + println(foo(10)) +} diff --git a/test/pending/run/reify_closures10.check b/test/pending/run/reify_closures10.check new file mode 100644 index 0000000000..fd3c81a4d7 --- /dev/null +++ b/test/pending/run/reify_closures10.check @@ -0,0 +1,2 @@ +5 +5 diff --git a/test/pending/run/reify_closures10.scala b/test/pending/run/reify_closures10.scala new file mode 100644 index 0000000000..d0f895ae4d --- /dev/null +++ b/test/pending/run/reify_closures10.scala @@ -0,0 +1,15 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val x = 2 + val y = 3 + val code = lift{println(x + y); x + y} + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + println(toolbox.runExpr(ttree)) +} diff --git a/test/pending/run/reify_closures11.check b/test/pending/run/reify_closures11.check new file mode 100644 index 0000000000..d8263ee986 --- /dev/null +++ b/test/pending/run/reify_closures11.check @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/test/pending/run/reify_closures11.scala b/test/pending/run/reify_closures11.scala new file mode 100644 index 0000000000..42053bd029 --- /dev/null +++ b/test/pending/run/reify_closures11.scala @@ -0,0 +1,18 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def fun() = { + def z() = 2 + lift{z} + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun().tree) + val dyn = toolbox.runExpr(ttree) + val foo = dyn.asInstanceOf[Int] + println(foo) +} diff --git a/test/pending/run/reify_complex.check b/test/pending/run/reify_complex.check deleted file mode 100644 index 7df35e33a0..0000000000 --- a/test/pending/run/reify_complex.check +++ /dev/null @@ -1 +0,0 @@ -3.0+4.0*i diff --git a/test/pending/run/reify_complex.scala b/test/pending/run/reify_complex.scala deleted file mode 100644 index aae4d558cf..0000000000 --- a/test/pending/run/reify_complex.scala +++ /dev/null @@ -1,31 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - class Complex(val re: Double, val im: Double) { - def + (that: Complex) = - new Complex(re + that.re, im + that.im) - def - (that: Complex) = - new Complex(re - that.re, im - that.im) - def * (that: Complex) = - new Complex(re * that.re - im * that.im, - re * that.im + im * that.re) - def / (that: Complex) = { - val denom = that.re * that.re + that.im * that.im - new Complex((re * that.re + im * that.im) / denom, - (im * that.re - re * that.im) / denom) - } - override def toString = - re + (if (im < 0) "-" + (-im) else "+" + im) + "*i" - } - val x = new Complex(2, 1); val y = new Complex(1, 3) - println(x + y) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/reify_extendbuiltins.check b/test/pending/run/reify_extendbuiltins.check deleted file mode 100644 index a48033a30d..0000000000 --- a/test/pending/run/reify_extendbuiltins.check +++ /dev/null @@ -1 +0,0 @@ -10! = 3628800 diff --git a/test/pending/run/reify_extendbuiltins.scala b/test/pending/run/reify_extendbuiltins.scala deleted file mode 100644 index 57acd699ff..0000000000 --- a/test/pending/run/reify_extendbuiltins.scala +++ /dev/null @@ -1,21 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - def fact(n: Int): BigInt = - if (n == 0) 1 else fact(n-1) * n - class Factorizer(n: Int) { - def ! = fact(n) - } - implicit def int2fact(n: Int) = new Factorizer(n) - - println("10! = " + (10!)) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/reify_sort1.check b/test/pending/run/reify_sort1.check deleted file mode 100644 index 0d30805141..0000000000 --- a/test/pending/run/reify_sort1.check +++ /dev/null @@ -1,2 +0,0 @@ -List(6, 2, 8, 5, 1) -List(1, 2, 5, 6, 8) diff --git a/test/pending/run/reify_sort1.scala b/test/pending/run/reify_sort1.scala deleted file mode 100644 index 42f4c824a5..0000000000 --- a/test/pending/run/reify_sort1.scala +++ /dev/null @@ -1,27 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - def sort(a: List[Int]): List[Int] = { - if (a.length < 2) - a - else { - val pivot = a(a.length / 2) - sort(a.filter(_ < pivot)) ::: - a.filter(_ == pivot) ::: - sort(a.filter(_ > pivot)) - } - } - - val xs = List(6, 2, 8, 5, 1) - println(xs) - println(sort(xs)) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/reify_this.check b/test/pending/run/reify_this.check new file mode 100644 index 0000000000..af3d0652a9 --- /dev/null +++ b/test/pending/run/reify_this.check @@ -0,0 +1,5 @@ +foo +false +2 +bar +2 \ No newline at end of file diff --git a/test/pending/run/reify_this.scala b/test/pending/run/reify_this.scala new file mode 100644 index 0000000000..38ef72b6eb --- /dev/null +++ b/test/pending/run/reify_this.scala @@ -0,0 +1,31 @@ +import scala.reflect._ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +trait Eval { + def eval(code: Code[_]): Any = eval(code.tree) + + def eval(tree: Tree): Any = { + val settings = new Settings + val reporter = new ConsoleReporter(settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(tree) + toolbox.runExpr(ttree) + } +} + +object Test extends App with Eval { + // select a value from package + eval(lift{println("foo")}) + eval(lift{println((new Object).toString == (new Object).toString)}) + + // select a type from package + eval(lift{val x: Any = 2; println(x)}) + eval(lift{val x: Object = "bar"; println(x)}) + + // select a value from module + val x = 2 + eval(lift{println(x)}) +} diff --git a/test/pending/run/t5269.check b/test/pending/run/t5269.check deleted file mode 100644 index 0cfbf08886..0000000000 --- a/test/pending/run/t5269.check +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/test/pending/run/t5269.scala b/test/pending/run/t5269.scala deleted file mode 100644 index a30509f3fe..0000000000 --- a/test/pending/run/t5269.scala +++ /dev/null @@ -1,22 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - trait Z { - val z = 2 - } - - class X extends Z { - def println() = Predef.println(z) - } - - new X().println() - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/t5274_1.check b/test/pending/run/t5274_1.check deleted file mode 100644 index fca8bc3d3e..0000000000 --- a/test/pending/run/t5274_1.check +++ /dev/null @@ -1,3 +0,0 @@ -50! = 30414093201713378043612608166064768844377641568960512000000000000 -49! = 608281864034267560872252163321295376887552831379210240000000000 -50!/49! = 50 diff --git a/test/pending/run/t5274_1.scala b/test/pending/run/t5274_1.scala deleted file mode 100644 index c501172518..0000000000 --- a/test/pending/run/t5274_1.scala +++ /dev/null @@ -1,20 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - def factorial(n: BigInt): BigInt = - if (n == 0) 1 else n * factorial(n-1) - - val f50 = factorial(50); val f49 = factorial(49) - println("50! = " + f50) - println("49! = " + f49) - println("50!/49! = " + (f50 / f49)) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/t5275.check b/test/pending/run/t5275.check deleted file mode 100644 index 0cfbf08886..0000000000 --- a/test/pending/run/t5275.check +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/test/pending/run/t5275.scala b/test/pending/run/t5275.scala deleted file mode 100644 index d419834ded..0000000000 --- a/test/pending/run/t5275.scala +++ /dev/null @@ -1,15 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - class C(val foo: Int) - println(new C(2).foo) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/t5277_1.check b/test/pending/run/t5277_1.check deleted file mode 100644 index a48033a30d..0000000000 --- a/test/pending/run/t5277_1.check +++ /dev/null @@ -1 +0,0 @@ -10! = 3628800 diff --git a/test/pending/run/t5277_1.scala b/test/pending/run/t5277_1.scala deleted file mode 100644 index 57acd699ff..0000000000 --- a/test/pending/run/t5277_1.scala +++ /dev/null @@ -1,21 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - def fact(n: Int): BigInt = - if (n == 0) 1 else fact(n-1) * n - class Factorizer(n: Int) { - def ! = fact(n) - } - implicit def int2fact(n: Int) = new Factorizer(n) - - println("10! = " + (10!)) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/t5277_2.check b/test/pending/run/t5277_2.check deleted file mode 100644 index 5f1d0ecea5..0000000000 --- a/test/pending/run/t5277_2.check +++ /dev/null @@ -1,2 +0,0 @@ -2 -1 diff --git a/test/pending/run/t5277_2.scala b/test/pending/run/t5277_2.scala deleted file mode 100644 index 67b6b000bc..0000000000 --- a/test/pending/run/t5277_2.scala +++ /dev/null @@ -1,18 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - def p(implicit i: Int) = print(i) - implicit val v = 2 - - println(p) - println(p(1)) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/t5415.check b/test/pending/run/t5415.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/pending/run/t5415.scala b/test/pending/run/t5415.scala new file mode 100644 index 0000000000..3db356da86 --- /dev/null +++ b/test/pending/run/t5415.scala @@ -0,0 +1,14 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import scala.reflect.runtime.Mirror.ToolBox + +object Test extends App{ + case class Queryable2[T]() { def filter(predicate: T => Boolean) = ??? } + trait CoffeesTable{ def sales : Int } + val q = Queryable2[CoffeesTable]() + val code = scala.reflect.Code.lift{q.filter(_.sales > 5)} + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) +} -- cgit v1.2.3 From 363f8af6a8c157485a644d00d75e2df10e71e661 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 2 Feb 2012 15:29:55 +0100 Subject: Fixes reifyThis --- src/compiler/scala/reflect/internal/StdNames.scala | 1 + src/compiler/scala/reflect/internal/Trees.scala | 2 - .../scala/tools/nsc/transform/LiftCode.scala | 15 +++++- src/library/scala/reflect/api/Trees.scala | 3 ++ test/files/run/reify_closure1.check | 2 + test/files/run/reify_closure1.scala | 20 ++++++++ test/files/run/reify_closure2a.check | 2 + test/files/run/reify_closure2a.scala | 20 ++++++++ test/files/run/reify_closure3a.check | 2 + test/files/run/reify_closure3a.scala | 22 +++++++++ test/files/run/reify_closure4a.check | 2 + test/files/run/reify_closure4a.scala | 22 +++++++++ test/files/run/reify_closure5a.check | 2 + test/files/run/reify_closure5a.scala | 20 ++++++++ test/files/run/reify_closure6.check | 7 +++ test/files/run/reify_closure6.scala | 28 +++++++++++ test/files/run/reify_closure7.check | 6 +++ test/files/run/reify_closure7.scala | 32 ++++++++++++ test/files/run/reify_closure8a.check | 1 + test/files/run/reify_closure8a.scala | 17 +++++++ test/files/run/reify_closures10.check | 2 + test/files/run/reify_closures10.scala | 15 ++++++ test/files/run/reify_implicits.check | 1 + test/files/run/reify_implicits.scala | 21 ++++++++ test/files/run/reify_sort.check | 2 + test/files/run/reify_sort.scala | 57 ++++++++++++++++++++++ test/files/run/reify_this.check | 5 ++ test/files/run/reify_this.scala | 31 ++++++++++++ test/files/run/t5274_2.check | 2 + test/files/run/t5274_2.scala | 57 ++++++++++++++++++++++ test/files/run/t5279.check | 1 + test/files/run/t5279.scala | 14 ++++++ test/files/run/t5415.check | 0 test/files/run/t5415.scala | 14 ++++++ test/pending/run/reify_closure1.check | 2 - test/pending/run/reify_closure1.scala | 20 -------- test/pending/run/reify_closure2a.check | 2 - test/pending/run/reify_closure2a.scala | 20 -------- test/pending/run/reify_closure3a.check | 2 - test/pending/run/reify_closure3a.scala | 22 --------- test/pending/run/reify_closure4a.check | 2 - test/pending/run/reify_closure4a.scala | 22 --------- test/pending/run/reify_closure5a.check | 2 - test/pending/run/reify_closure5a.scala | 20 -------- test/pending/run/reify_closure6.check | 7 --- test/pending/run/reify_closure6.scala | 28 ----------- test/pending/run/reify_closure7.check | 6 --- test/pending/run/reify_closure7.scala | 32 ------------ test/pending/run/reify_closure8a.check | 1 - test/pending/run/reify_closure8a.scala | 17 ------- test/pending/run/reify_closures10.check | 2 - test/pending/run/reify_closures10.scala | 15 ------ test/pending/run/reify_implicits.check | 1 - test/pending/run/reify_implicits.scala | 21 -------- test/pending/run/reify_sort.check | 2 - test/pending/run/reify_sort.scala | 57 ---------------------- test/pending/run/reify_this.check | 5 -- test/pending/run/reify_this.scala | 31 ------------ test/pending/run/t5274_2.check | 2 - test/pending/run/t5274_2.scala | 57 ---------------------- test/pending/run/t5279.check | 1 - test/pending/run/t5279.scala | 14 ------ test/pending/run/t5415.check | 0 test/pending/run/t5415.scala | 14 ------ 64 files changed, 444 insertions(+), 431 deletions(-) create mode 100644 test/files/run/reify_closure1.check create mode 100644 test/files/run/reify_closure1.scala create mode 100644 test/files/run/reify_closure2a.check create mode 100644 test/files/run/reify_closure2a.scala create mode 100644 test/files/run/reify_closure3a.check create mode 100644 test/files/run/reify_closure3a.scala create mode 100644 test/files/run/reify_closure4a.check create mode 100644 test/files/run/reify_closure4a.scala create mode 100644 test/files/run/reify_closure5a.check create mode 100644 test/files/run/reify_closure5a.scala create mode 100644 test/files/run/reify_closure6.check create mode 100644 test/files/run/reify_closure6.scala create mode 100644 test/files/run/reify_closure7.check create mode 100644 test/files/run/reify_closure7.scala create mode 100644 test/files/run/reify_closure8a.check create mode 100644 test/files/run/reify_closure8a.scala create mode 100644 test/files/run/reify_closures10.check create mode 100644 test/files/run/reify_closures10.scala create mode 100644 test/files/run/reify_implicits.check create mode 100644 test/files/run/reify_implicits.scala create mode 100644 test/files/run/reify_sort.check create mode 100644 test/files/run/reify_sort.scala create mode 100644 test/files/run/reify_this.check create mode 100644 test/files/run/reify_this.scala create mode 100644 test/files/run/t5274_2.check create mode 100644 test/files/run/t5274_2.scala create mode 100644 test/files/run/t5279.check create mode 100644 test/files/run/t5279.scala create mode 100644 test/files/run/t5415.check create mode 100644 test/files/run/t5415.scala delete mode 100644 test/pending/run/reify_closure1.check delete mode 100644 test/pending/run/reify_closure1.scala delete mode 100644 test/pending/run/reify_closure2a.check delete mode 100644 test/pending/run/reify_closure2a.scala delete mode 100644 test/pending/run/reify_closure3a.check delete mode 100644 test/pending/run/reify_closure3a.scala delete mode 100644 test/pending/run/reify_closure4a.check delete mode 100644 test/pending/run/reify_closure4a.scala delete mode 100644 test/pending/run/reify_closure5a.check delete mode 100644 test/pending/run/reify_closure5a.scala delete mode 100644 test/pending/run/reify_closure6.check delete mode 100644 test/pending/run/reify_closure6.scala delete mode 100644 test/pending/run/reify_closure7.check delete mode 100644 test/pending/run/reify_closure7.scala delete mode 100644 test/pending/run/reify_closure8a.check delete mode 100644 test/pending/run/reify_closure8a.scala delete mode 100644 test/pending/run/reify_closures10.check delete mode 100644 test/pending/run/reify_closures10.scala delete mode 100644 test/pending/run/reify_implicits.check delete mode 100644 test/pending/run/reify_implicits.scala delete mode 100644 test/pending/run/reify_sort.check delete mode 100644 test/pending/run/reify_sort.scala delete mode 100644 test/pending/run/reify_this.check delete mode 100644 test/pending/run/reify_this.scala delete mode 100644 test/pending/run/t5274_2.check delete mode 100644 test/pending/run/t5274_2.scala delete mode 100644 test/pending/run/t5279.check delete mode 100644 test/pending/run/t5279.scala delete mode 100644 test/pending/run/t5415.check delete mode 100644 test/pending/run/t5415.scala (limited to 'src/compiler') 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/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index 5bb0c98bfb..ca7801ac9d 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -251,8 +251,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/tools/nsc/transform/LiftCode.scala b/src/compiler/scala/tools/nsc/transform/LiftCode.scala index 197a52f011..d0ed92f8ba 100644 --- a/src/compiler/scala/tools/nsc/transform/LiftCode.scala +++ b/src/compiler/scala/tools/nsc/transform/LiftCode.scala @@ -460,8 +460,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 = 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 . */ case class Select(qualifier: Tree, name: Name) extends RefTree diff --git a/test/files/run/reify_closure1.check b/test/files/run/reify_closure1.check new file mode 100644 index 0000000000..b2f7f08c17 --- /dev/null +++ b/test/files/run/reify_closure1.check @@ -0,0 +1,2 @@ +10 +10 diff --git a/test/files/run/reify_closure1.scala b/test/files/run/reify_closure1.scala new file mode 100644 index 0000000000..825a38dc1d --- /dev/null +++ b/test/files/run/reify_closure1.scala @@ -0,0 +1,20 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def foo[T](ys: List[T]): Int => Int = { + val fun: reflect.Code[Int => Int] = x => { + x + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int => Int] + } + + println(foo(List(1, 2, 3))(10)) + println(foo(List(1, 2, 3, 4))(10)) +} diff --git a/test/files/run/reify_closure2a.check b/test/files/run/reify_closure2a.check new file mode 100644 index 0000000000..c1f3abd7e6 --- /dev/null +++ b/test/files/run/reify_closure2a.check @@ -0,0 +1,2 @@ +11 +12 diff --git a/test/files/run/reify_closure2a.scala b/test/files/run/reify_closure2a.scala new file mode 100644 index 0000000000..b88bec005d --- /dev/null +++ b/test/files/run/reify_closure2a.scala @@ -0,0 +1,20 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def foo(y: Int): Int => Int = { + val fun: reflect.Code[Int => Int] = x => { + x + y + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int => Int] + } + + println(foo(1)(10)) + println(foo(2)(10)) +} diff --git a/test/files/run/reify_closure3a.check b/test/files/run/reify_closure3a.check new file mode 100644 index 0000000000..c1f3abd7e6 --- /dev/null +++ b/test/files/run/reify_closure3a.check @@ -0,0 +1,2 @@ +11 +12 diff --git a/test/files/run/reify_closure3a.scala b/test/files/run/reify_closure3a.scala new file mode 100644 index 0000000000..6414fa58a3 --- /dev/null +++ b/test/files/run/reify_closure3a.scala @@ -0,0 +1,22 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def foo(y: Int): Int => Int = { + def y1 = y + + val fun: reflect.Code[Int => Int] = x => { + x + y1 + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int => Int] + } + + println(foo(1)(10)) + println(foo(2)(10)) +} diff --git a/test/files/run/reify_closure4a.check b/test/files/run/reify_closure4a.check new file mode 100644 index 0000000000..c1f3abd7e6 --- /dev/null +++ b/test/files/run/reify_closure4a.check @@ -0,0 +1,2 @@ +11 +12 diff --git a/test/files/run/reify_closure4a.scala b/test/files/run/reify_closure4a.scala new file mode 100644 index 0000000000..99e9d82706 --- /dev/null +++ b/test/files/run/reify_closure4a.scala @@ -0,0 +1,22 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def foo(y: Int): Int => Int = { + val y1 = y + + val fun: reflect.Code[Int => Int] = x => { + x + y1 + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int => Int] + } + + println(foo(1)(10)) + println(foo(2)(10)) +} diff --git a/test/files/run/reify_closure5a.check b/test/files/run/reify_closure5a.check new file mode 100644 index 0000000000..df9e19c591 --- /dev/null +++ b/test/files/run/reify_closure5a.check @@ -0,0 +1,2 @@ +13 +14 diff --git a/test/files/run/reify_closure5a.scala b/test/files/run/reify_closure5a.scala new file mode 100644 index 0000000000..0ac53d5479 --- /dev/null +++ b/test/files/run/reify_closure5a.scala @@ -0,0 +1,20 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + def foo[T](ys: List[T]): Int => Int = { + val fun: reflect.Code[Int => Int] = x => { + x + ys.length + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int => Int] + } + + println(foo(List(1, 2, 3))(10)) + println(foo(List(1, 2, 3, 4))(10)) +} diff --git a/test/files/run/reify_closure6.check b/test/files/run/reify_closure6.check new file mode 100644 index 0000000000..b9de4c6baf --- /dev/null +++ b/test/files/run/reify_closure6.check @@ -0,0 +1,7 @@ +q = 1 +y = 1 +first invocation = 15 +q = 2 +y = 1 +second invocation = 17 +q after second invocation = 2 \ No newline at end of file diff --git a/test/files/run/reify_closure6.scala b/test/files/run/reify_closure6.scala new file mode 100644 index 0000000000..54f1791bf2 --- /dev/null +++ b/test/files/run/reify_closure6.scala @@ -0,0 +1,28 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + var q = 0 + def foo[T](ys: List[T]): Int => Int = { + val z = 1 + var y = 0 + val fun: reflect.Code[Int => Int] = x => { + y += 1 + q += 1 + println("q = " + q) + println("y = " + y) + x + ys.length * z + q + y + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + dyn.asInstanceOf[Int => Int] + } + + println("first invocation = " + foo(List(1, 2, 3))(10)) + println("second invocation = " + foo(List(1, 2, 3, 4))(10)) + println("q after second invocation = " + q) +} \ No newline at end of file diff --git a/test/files/run/reify_closure7.check b/test/files/run/reify_closure7.check new file mode 100644 index 0000000000..bf58b52bce --- /dev/null +++ b/test/files/run/reify_closure7.check @@ -0,0 +1,6 @@ +q = 1 +y = 1 +first invocation = 15 +q = 2 +y = 2 +second invocation = 17 diff --git a/test/files/run/reify_closure7.scala b/test/files/run/reify_closure7.scala new file mode 100644 index 0000000000..8933df23fa --- /dev/null +++ b/test/files/run/reify_closure7.scala @@ -0,0 +1,32 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + var q = 0 + var clo: Int => Int = null + def foo[T](ys: List[T]): Int => Int = { + val z = 1 + var y = 0 + val fun: reflect.Code[Int => Int] = x => { + y += 1 + q += 1 + println("q = " + q) + println("y = " + y) + x + ys.length * z + q + y + } + + if (clo == null) { + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(fun.tree) + val dyn = toolbox.runExpr(ttree) + clo = dyn.asInstanceOf[Int => Int] + } + + clo + } + + println("first invocation = " + foo(List(1, 2, 3))(10)) + println("second invocation = " + foo(List(1, 2, 3, 4))(10)) +} diff --git a/test/files/run/reify_closure8a.check b/test/files/run/reify_closure8a.check new file mode 100644 index 0000000000..9a037142aa --- /dev/null +++ b/test/files/run/reify_closure8a.check @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/test/files/run/reify_closure8a.scala b/test/files/run/reify_closure8a.scala new file mode 100644 index 0000000000..5e54bfc8c7 --- /dev/null +++ b/test/files/run/reify_closure8a.scala @@ -0,0 +1,17 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + class Foo(val y: Int) { + def fun = lift{y} + } + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(new Foo(10).fun.tree) + val dyn = toolbox.runExpr(ttree) + val foo = dyn.asInstanceOf[Int] + println(foo) +} diff --git a/test/files/run/reify_closures10.check b/test/files/run/reify_closures10.check new file mode 100644 index 0000000000..fd3c81a4d7 --- /dev/null +++ b/test/files/run/reify_closures10.check @@ -0,0 +1,2 @@ +5 +5 diff --git a/test/files/run/reify_closures10.scala b/test/files/run/reify_closures10.scala new file mode 100644 index 0000000000..d0f895ae4d --- /dev/null +++ b/test/files/run/reify_closures10.scala @@ -0,0 +1,15 @@ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val x = 2 + val y = 3 + val code = lift{println(x + y); x + y} + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + println(toolbox.runExpr(ttree)) +} diff --git a/test/files/run/reify_implicits.check b/test/files/run/reify_implicits.check new file mode 100644 index 0000000000..e3aeb20f6b --- /dev/null +++ b/test/files/run/reify_implicits.check @@ -0,0 +1 @@ +x = List(1, 2, 3, 4) diff --git a/test/files/run/reify_implicits.scala b/test/files/run/reify_implicits.scala new file mode 100644 index 0000000000..a15cef9c97 --- /dev/null +++ b/test/files/run/reify_implicits.scala @@ -0,0 +1,21 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + implicit def arrayWrapper[A : ClassManifest](x: Array[A]) = + new { + def sort(p: (A, A) => Boolean) = { + util.Sorting.stableSort(x, p); x + } + } + val x = Array(2, 3, 1, 4) + println("x = "+ x.sort((x: Int, y: Int) => x < y).toList) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/reify_sort.check b/test/files/run/reify_sort.check new file mode 100644 index 0000000000..375536cc29 --- /dev/null +++ b/test/files/run/reify_sort.check @@ -0,0 +1,2 @@ +[6,2,8,5,1] +[1,2,5,6,8] diff --git a/test/files/run/reify_sort.scala b/test/files/run/reify_sort.scala new file mode 100644 index 0000000000..42991fe5d2 --- /dev/null +++ b/test/files/run/reify_sort.scala @@ -0,0 +1,57 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + /** Nested methods can use and even update everything + * visible in their scope (including local variables or + * arguments of enclosing methods). + */ + def sort(a: Array[Int]) { + + def swap(i: Int, j: Int) { + val t = a(i); a(i) = a(j); a(j) = t + } + + def sort1(l: Int, r: Int) { + val pivot = a((l + r) / 2) + var i = l + var j = r + while (i <= j) { + while (a(i) < pivot) i += 1 + while (a(j) > pivot) j -= 1 + if (i <= j) { + swap(i, j) + i += 1 + j -= 1 + } + } + if (l < j) sort1(l, j) + if (j < r) sort1(i, r) + } + + if (a.length > 0) + sort1(0, a.length - 1) + } + + def println(ar: Array[Int]) { + def print1 = { + def iter(i: Int): String = + ar(i) + (if (i < ar.length-1) "," + iter(i+1) else "") + if (ar.length == 0) "" else iter(0) + } + Console.println("[" + print1 + "]") + } + + val ar = Array(6, 2, 8, 5, 1) + println(ar) + sort(ar) + println(ar) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/reify_this.check b/test/files/run/reify_this.check new file mode 100644 index 0000000000..af3d0652a9 --- /dev/null +++ b/test/files/run/reify_this.check @@ -0,0 +1,5 @@ +foo +false +2 +bar +2 \ No newline at end of file diff --git a/test/files/run/reify_this.scala b/test/files/run/reify_this.scala new file mode 100644 index 0000000000..38ef72b6eb --- /dev/null +++ b/test/files/run/reify_this.scala @@ -0,0 +1,31 @@ +import scala.reflect._ +import scala.reflect.Code._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +trait Eval { + def eval(code: Code[_]): Any = eval(code.tree) + + def eval(tree: Tree): Any = { + val settings = new Settings + val reporter = new ConsoleReporter(settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(tree) + toolbox.runExpr(ttree) + } +} + +object Test extends App with Eval { + // select a value from package + eval(lift{println("foo")}) + eval(lift{println((new Object).toString == (new Object).toString)}) + + // select a type from package + eval(lift{val x: Any = 2; println(x)}) + eval(lift{val x: Object = "bar"; println(x)}) + + // select a value from module + val x = 2 + eval(lift{println(x)}) +} diff --git a/test/files/run/t5274_2.check b/test/files/run/t5274_2.check new file mode 100644 index 0000000000..375536cc29 --- /dev/null +++ b/test/files/run/t5274_2.check @@ -0,0 +1,2 @@ +[6,2,8,5,1] +[1,2,5,6,8] diff --git a/test/files/run/t5274_2.scala b/test/files/run/t5274_2.scala new file mode 100644 index 0000000000..42991fe5d2 --- /dev/null +++ b/test/files/run/t5274_2.scala @@ -0,0 +1,57 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + /** Nested methods can use and even update everything + * visible in their scope (including local variables or + * arguments of enclosing methods). + */ + def sort(a: Array[Int]) { + + def swap(i: Int, j: Int) { + val t = a(i); a(i) = a(j); a(j) = t + } + + def sort1(l: Int, r: Int) { + val pivot = a((l + r) / 2) + var i = l + var j = r + while (i <= j) { + while (a(i) < pivot) i += 1 + while (a(j) > pivot) j -= 1 + if (i <= j) { + swap(i, j) + i += 1 + j -= 1 + } + } + if (l < j) sort1(l, j) + if (j < r) sort1(i, r) + } + + if (a.length > 0) + sort1(0, a.length - 1) + } + + def println(ar: Array[Int]) { + def print1 = { + def iter(i: Int): String = + ar(i) + (if (i < ar.length-1) "," + iter(i+1) else "") + if (ar.length == 0) "" else iter(0) + } + Console.println("[" + print1 + "]") + } + + val ar = Array(6, 2, 8, 5, 1) + println(ar) + sort(ar) + println(ar) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5279.check b/test/files/run/t5279.check new file mode 100644 index 0000000000..f599e28b8a --- /dev/null +++ b/test/files/run/t5279.check @@ -0,0 +1 @@ +10 diff --git a/test/files/run/t5279.scala b/test/files/run/t5279.scala new file mode 100644 index 0000000000..39e7dd2c66 --- /dev/null +++ b/test/files/run/t5279.scala @@ -0,0 +1,14 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import reflect.runtime.Mirror.ToolBox + +object Test extends App { + val code = scala.reflect.Code.lift{ + println(new Integer(10)) + }; + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) + toolbox.runExpr(ttree) +} diff --git a/test/files/run/t5415.check b/test/files/run/t5415.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/files/run/t5415.scala b/test/files/run/t5415.scala new file mode 100644 index 0000000000..3db356da86 --- /dev/null +++ b/test/files/run/t5415.scala @@ -0,0 +1,14 @@ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.Settings +import scala.reflect.runtime.Mirror.ToolBox + +object Test extends App{ + case class Queryable2[T]() { def filter(predicate: T => Boolean) = ??? } + trait CoffeesTable{ def sales : Int } + val q = Queryable2[CoffeesTable]() + val code = scala.reflect.Code.lift{q.filter(_.sales > 5)} + + val reporter = new ConsoleReporter(new Settings) + val toolbox = new ToolBox(reporter) + val ttree = toolbox.typeCheck(code.tree) +} diff --git a/test/pending/run/reify_closure1.check b/test/pending/run/reify_closure1.check deleted file mode 100644 index b2f7f08c17..0000000000 --- a/test/pending/run/reify_closure1.check +++ /dev/null @@ -1,2 +0,0 @@ -10 -10 diff --git a/test/pending/run/reify_closure1.scala b/test/pending/run/reify_closure1.scala deleted file mode 100644 index 825a38dc1d..0000000000 --- a/test/pending/run/reify_closure1.scala +++ /dev/null @@ -1,20 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - def foo[T](ys: List[T]): Int => Int = { - val fun: reflect.Code[Int => Int] = x => { - x - } - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(fun.tree) - val dyn = toolbox.runExpr(ttree) - dyn.asInstanceOf[Int => Int] - } - - println(foo(List(1, 2, 3))(10)) - println(foo(List(1, 2, 3, 4))(10)) -} diff --git a/test/pending/run/reify_closure2a.check b/test/pending/run/reify_closure2a.check deleted file mode 100644 index c1f3abd7e6..0000000000 --- a/test/pending/run/reify_closure2a.check +++ /dev/null @@ -1,2 +0,0 @@ -11 -12 diff --git a/test/pending/run/reify_closure2a.scala b/test/pending/run/reify_closure2a.scala deleted file mode 100644 index b88bec005d..0000000000 --- a/test/pending/run/reify_closure2a.scala +++ /dev/null @@ -1,20 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - def foo(y: Int): Int => Int = { - val fun: reflect.Code[Int => Int] = x => { - x + y - } - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(fun.tree) - val dyn = toolbox.runExpr(ttree) - dyn.asInstanceOf[Int => Int] - } - - println(foo(1)(10)) - println(foo(2)(10)) -} diff --git a/test/pending/run/reify_closure3a.check b/test/pending/run/reify_closure3a.check deleted file mode 100644 index c1f3abd7e6..0000000000 --- a/test/pending/run/reify_closure3a.check +++ /dev/null @@ -1,2 +0,0 @@ -11 -12 diff --git a/test/pending/run/reify_closure3a.scala b/test/pending/run/reify_closure3a.scala deleted file mode 100644 index 6414fa58a3..0000000000 --- a/test/pending/run/reify_closure3a.scala +++ /dev/null @@ -1,22 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - def foo(y: Int): Int => Int = { - def y1 = y - - val fun: reflect.Code[Int => Int] = x => { - x + y1 - } - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(fun.tree) - val dyn = toolbox.runExpr(ttree) - dyn.asInstanceOf[Int => Int] - } - - println(foo(1)(10)) - println(foo(2)(10)) -} diff --git a/test/pending/run/reify_closure4a.check b/test/pending/run/reify_closure4a.check deleted file mode 100644 index c1f3abd7e6..0000000000 --- a/test/pending/run/reify_closure4a.check +++ /dev/null @@ -1,2 +0,0 @@ -11 -12 diff --git a/test/pending/run/reify_closure4a.scala b/test/pending/run/reify_closure4a.scala deleted file mode 100644 index 99e9d82706..0000000000 --- a/test/pending/run/reify_closure4a.scala +++ /dev/null @@ -1,22 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - def foo(y: Int): Int => Int = { - val y1 = y - - val fun: reflect.Code[Int => Int] = x => { - x + y1 - } - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(fun.tree) - val dyn = toolbox.runExpr(ttree) - dyn.asInstanceOf[Int => Int] - } - - println(foo(1)(10)) - println(foo(2)(10)) -} diff --git a/test/pending/run/reify_closure5a.check b/test/pending/run/reify_closure5a.check deleted file mode 100644 index df9e19c591..0000000000 --- a/test/pending/run/reify_closure5a.check +++ /dev/null @@ -1,2 +0,0 @@ -13 -14 diff --git a/test/pending/run/reify_closure5a.scala b/test/pending/run/reify_closure5a.scala deleted file mode 100644 index 0ac53d5479..0000000000 --- a/test/pending/run/reify_closure5a.scala +++ /dev/null @@ -1,20 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - def foo[T](ys: List[T]): Int => Int = { - val fun: reflect.Code[Int => Int] = x => { - x + ys.length - } - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(fun.tree) - val dyn = toolbox.runExpr(ttree) - dyn.asInstanceOf[Int => Int] - } - - println(foo(List(1, 2, 3))(10)) - println(foo(List(1, 2, 3, 4))(10)) -} diff --git a/test/pending/run/reify_closure6.check b/test/pending/run/reify_closure6.check deleted file mode 100644 index e521ea874d..0000000000 --- a/test/pending/run/reify_closure6.check +++ /dev/null @@ -1,7 +0,0 @@ -q = 1 -y = 1 -first invocation = 15 -q = 2 -y = 1 -second invocation = 17 -q after second invocation = 2 diff --git a/test/pending/run/reify_closure6.scala b/test/pending/run/reify_closure6.scala deleted file mode 100644 index 43ddfde28d..0000000000 --- a/test/pending/run/reify_closure6.scala +++ /dev/null @@ -1,28 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - var q = 0 - def foo[T](ys: List[T]): Int => Int = { - val z = 1 - var y = 0 - val fun: reflect.Code[Int => Int] = x => { - y += 1 - q += 1 - println("q = " + q) - println("y = " + y) - x + ys.length * z + q + y - } - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(fun.tree) - val dyn = toolbox.runExpr(ttree) - dyn.asInstanceOf[Int => Int] - } - - println("first invocation = " + foo(List(1, 2, 3))(10)) - println("second invocation = " + foo(List(1, 2, 3, 4))(10)) - println("q after second invocation = " + q) -} diff --git a/test/pending/run/reify_closure7.check b/test/pending/run/reify_closure7.check deleted file mode 100644 index bf58b52bce..0000000000 --- a/test/pending/run/reify_closure7.check +++ /dev/null @@ -1,6 +0,0 @@ -q = 1 -y = 1 -first invocation = 15 -q = 2 -y = 2 -second invocation = 17 diff --git a/test/pending/run/reify_closure7.scala b/test/pending/run/reify_closure7.scala deleted file mode 100644 index 8933df23fa..0000000000 --- a/test/pending/run/reify_closure7.scala +++ /dev/null @@ -1,32 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - var q = 0 - var clo: Int => Int = null - def foo[T](ys: List[T]): Int => Int = { - val z = 1 - var y = 0 - val fun: reflect.Code[Int => Int] = x => { - y += 1 - q += 1 - println("q = " + q) - println("y = " + y) - x + ys.length * z + q + y - } - - if (clo == null) { - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(fun.tree) - val dyn = toolbox.runExpr(ttree) - clo = dyn.asInstanceOf[Int => Int] - } - - clo - } - - println("first invocation = " + foo(List(1, 2, 3))(10)) - println("second invocation = " + foo(List(1, 2, 3, 4))(10)) -} diff --git a/test/pending/run/reify_closure8a.check b/test/pending/run/reify_closure8a.check deleted file mode 100644 index 9a037142aa..0000000000 --- a/test/pending/run/reify_closure8a.check +++ /dev/null @@ -1 +0,0 @@ -10 \ No newline at end of file diff --git a/test/pending/run/reify_closure8a.scala b/test/pending/run/reify_closure8a.scala deleted file mode 100644 index 5e54bfc8c7..0000000000 --- a/test/pending/run/reify_closure8a.scala +++ /dev/null @@ -1,17 +0,0 @@ -import scala.reflect.Code._ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - class Foo(val y: Int) { - def fun = lift{y} - } - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(new Foo(10).fun.tree) - val dyn = toolbox.runExpr(ttree) - val foo = dyn.asInstanceOf[Int] - println(foo) -} diff --git a/test/pending/run/reify_closures10.check b/test/pending/run/reify_closures10.check deleted file mode 100644 index fd3c81a4d7..0000000000 --- a/test/pending/run/reify_closures10.check +++ /dev/null @@ -1,2 +0,0 @@ -5 -5 diff --git a/test/pending/run/reify_closures10.scala b/test/pending/run/reify_closures10.scala deleted file mode 100644 index d0f895ae4d..0000000000 --- a/test/pending/run/reify_closures10.scala +++ /dev/null @@ -1,15 +0,0 @@ -import scala.reflect.Code._ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val x = 2 - val y = 3 - val code = lift{println(x + y); x + y} - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - println(toolbox.runExpr(ttree)) -} diff --git a/test/pending/run/reify_implicits.check b/test/pending/run/reify_implicits.check deleted file mode 100644 index e3aeb20f6b..0000000000 --- a/test/pending/run/reify_implicits.check +++ /dev/null @@ -1 +0,0 @@ -x = List(1, 2, 3, 4) diff --git a/test/pending/run/reify_implicits.scala b/test/pending/run/reify_implicits.scala deleted file mode 100644 index a15cef9c97..0000000000 --- a/test/pending/run/reify_implicits.scala +++ /dev/null @@ -1,21 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - implicit def arrayWrapper[A : ClassManifest](x: Array[A]) = - new { - def sort(p: (A, A) => Boolean) = { - util.Sorting.stableSort(x, p); x - } - } - val x = Array(2, 3, 1, 4) - println("x = "+ x.sort((x: Int, y: Int) => x < y).toList) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/reify_sort.check b/test/pending/run/reify_sort.check deleted file mode 100644 index 375536cc29..0000000000 --- a/test/pending/run/reify_sort.check +++ /dev/null @@ -1,2 +0,0 @@ -[6,2,8,5,1] -[1,2,5,6,8] diff --git a/test/pending/run/reify_sort.scala b/test/pending/run/reify_sort.scala deleted file mode 100644 index 42991fe5d2..0000000000 --- a/test/pending/run/reify_sort.scala +++ /dev/null @@ -1,57 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - /** Nested methods can use and even update everything - * visible in their scope (including local variables or - * arguments of enclosing methods). - */ - def sort(a: Array[Int]) { - - def swap(i: Int, j: Int) { - val t = a(i); a(i) = a(j); a(j) = t - } - - def sort1(l: Int, r: Int) { - val pivot = a((l + r) / 2) - var i = l - var j = r - while (i <= j) { - while (a(i) < pivot) i += 1 - while (a(j) > pivot) j -= 1 - if (i <= j) { - swap(i, j) - i += 1 - j -= 1 - } - } - if (l < j) sort1(l, j) - if (j < r) sort1(i, r) - } - - if (a.length > 0) - sort1(0, a.length - 1) - } - - def println(ar: Array[Int]) { - def print1 = { - def iter(i: Int): String = - ar(i) + (if (i < ar.length-1) "," + iter(i+1) else "") - if (ar.length == 0) "" else iter(0) - } - Console.println("[" + print1 + "]") - } - - val ar = Array(6, 2, 8, 5, 1) - println(ar) - sort(ar) - println(ar) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/reify_this.check b/test/pending/run/reify_this.check deleted file mode 100644 index af3d0652a9..0000000000 --- a/test/pending/run/reify_this.check +++ /dev/null @@ -1,5 +0,0 @@ -foo -false -2 -bar -2 \ No newline at end of file diff --git a/test/pending/run/reify_this.scala b/test/pending/run/reify_this.scala deleted file mode 100644 index 38ef72b6eb..0000000000 --- a/test/pending/run/reify_this.scala +++ /dev/null @@ -1,31 +0,0 @@ -import scala.reflect._ -import scala.reflect.Code._ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -trait Eval { - def eval(code: Code[_]): Any = eval(code.tree) - - def eval(tree: Tree): Any = { - val settings = new Settings - val reporter = new ConsoleReporter(settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(tree) - toolbox.runExpr(ttree) - } -} - -object Test extends App with Eval { - // select a value from package - eval(lift{println("foo")}) - eval(lift{println((new Object).toString == (new Object).toString)}) - - // select a type from package - eval(lift{val x: Any = 2; println(x)}) - eval(lift{val x: Object = "bar"; println(x)}) - - // select a value from module - val x = 2 - eval(lift{println(x)}) -} diff --git a/test/pending/run/t5274_2.check b/test/pending/run/t5274_2.check deleted file mode 100644 index 375536cc29..0000000000 --- a/test/pending/run/t5274_2.check +++ /dev/null @@ -1,2 +0,0 @@ -[6,2,8,5,1] -[1,2,5,6,8] diff --git a/test/pending/run/t5274_2.scala b/test/pending/run/t5274_2.scala deleted file mode 100644 index 42991fe5d2..0000000000 --- a/test/pending/run/t5274_2.scala +++ /dev/null @@ -1,57 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - /** Nested methods can use and even update everything - * visible in their scope (including local variables or - * arguments of enclosing methods). - */ - def sort(a: Array[Int]) { - - def swap(i: Int, j: Int) { - val t = a(i); a(i) = a(j); a(j) = t - } - - def sort1(l: Int, r: Int) { - val pivot = a((l + r) / 2) - var i = l - var j = r - while (i <= j) { - while (a(i) < pivot) i += 1 - while (a(j) > pivot) j -= 1 - if (i <= j) { - swap(i, j) - i += 1 - j -= 1 - } - } - if (l < j) sort1(l, j) - if (j < r) sort1(i, r) - } - - if (a.length > 0) - sort1(0, a.length - 1) - } - - def println(ar: Array[Int]) { - def print1 = { - def iter(i: Int): String = - ar(i) + (if (i < ar.length-1) "," + iter(i+1) else "") - if (ar.length == 0) "" else iter(0) - } - Console.println("[" + print1 + "]") - } - - val ar = Array(6, 2, 8, 5, 1) - println(ar) - sort(ar) - println(ar) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/t5279.check b/test/pending/run/t5279.check deleted file mode 100644 index f599e28b8a..0000000000 --- a/test/pending/run/t5279.check +++ /dev/null @@ -1 +0,0 @@ -10 diff --git a/test/pending/run/t5279.scala b/test/pending/run/t5279.scala deleted file mode 100644 index 39e7dd2c66..0000000000 --- a/test/pending/run/t5279.scala +++ /dev/null @@ -1,14 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import reflect.runtime.Mirror.ToolBox - -object Test extends App { - val code = scala.reflect.Code.lift{ - println(new Integer(10)) - }; - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) - toolbox.runExpr(ttree) -} diff --git a/test/pending/run/t5415.check b/test/pending/run/t5415.check deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/pending/run/t5415.scala b/test/pending/run/t5415.scala deleted file mode 100644 index 3db356da86..0000000000 --- a/test/pending/run/t5415.scala +++ /dev/null @@ -1,14 +0,0 @@ -import scala.tools.nsc.reporters._ -import scala.tools.nsc.Settings -import scala.reflect.runtime.Mirror.ToolBox - -object Test extends App{ - case class Queryable2[T]() { def filter(predicate: T => Boolean) = ??? } - trait CoffeesTable{ def sales : Int } - val q = Queryable2[CoffeesTable]() - val code = scala.reflect.Code.lift{q.filter(_.sales > 5)} - - val reporter = new ConsoleReporter(new Settings) - val toolbox = new ToolBox(reporter) - val ttree = toolbox.typeCheck(code.tree) -} -- cgit v1.2.3 From 6f89da9e55315a2299ae8c4ab8c772936b862a85 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 26 Dec 2011 19:07:21 +0100 Subject: [vpm] factored out reusing treemakers (used by CSE) before, we were mutating treemakers in-place when they were reused no more mutation, and CSE is now self-contained interestingly, we were considering all FunTreeMakers as potentially reused, but only CondTreeMakers ever did anything with that flag should be clearer now that only those are ever reused simplified substonly treemaker a bit overall cleanup to prepare for switching to new-style detection of MatchStrategy delaying wrapping in function to simplify optimizing codegen logic --- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 238 +++++++++++---------- 1 file changed, 128 insertions(+), 110 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index b1e02cb062..49786813e8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -32,9 +32,6 @@ import Flags.{ CASE => _, _ } 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 @@ -139,6 +136,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 +213,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) ) @@ -651,6 +645,9 @@ defined class Foo */ lazy val optimizingCodeGen = matchingMonadType.typeSymbol eq OptionClass 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 +665,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 { @@ -682,20 +678,13 @@ defined class Foo */ atPos(body.pos)(substitution(pmgen.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 { @@ -706,44 +695,12 @@ defined class Foo */ 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._ + abstract class CondTreeMaker extends FreshFunTreeMaker { 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) - 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)(pmgen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) } /** @@ -754,12 +711,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 (pmgen.condOptimized(_, next)) getOrElse next + atPos(extractor.pos)( + if (extractorReturnsBoolean) pmgen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext)) + else pmgen.flatMap(extractor, nextBinder, substitution(condAndNext)) + ) + } override def toString = "X"+(extractor, nextBinder) } @@ -768,10 +726,7 @@ 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 - } + val cond = extraCond map (nullCheck AND _) getOrElse nullCheck pmgen.condOptimized(cond, substitution(next)) } @@ -957,6 +912,8 @@ defined class Foo */ override def toString = testedPath +" (<: && ==) "+ pt +"#"+ id } +//// CSE + /** 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) @@ -1031,7 +988,7 @@ 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) } @@ -1067,7 +1024,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 +1040,66 @@ 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) === pmgen.mkZero(b.info) } + } + + // TODO: finer-grained duplication + def chainBefore(next: Tree, pt: Type): Tree = + atPos(pos)(pmgen.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 pmgen.zero + } + } + + +//// DCE + // TODO: non-trivial dead-code elimination // e.g., the following match should compile to a simple instanceof: // case class Ident(name: String) @@ -1101,18 +1110,7 @@ defined class Foo */ } - 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 object SwitchablePattern { def unapply(pat: Tree) = pat match { case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches @@ -1205,6 +1203,20 @@ defined class Foo */ def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt) + + 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 @@ -1232,12 +1244,15 @@ defined class Foo */ val combinedCases = cases.map(combineExtractors(_, pt)).reduceLeft(pmgen.typedOrElse(optPt)) - toHoist = (for (treeMakers <- cases; tm <- treeMakers; hoisted <- tm.treesToHoist) yield hoisted).toList + toHoist = ( + for (treeMakers <- cases) + yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist} + ).flatten.flatten.toList - (pmgen.fun(scrutSym, combinedCases), hasDefault) + (combinedCases, hasDefault) } else (pmgen.zero, false) - val expr = pmgen.runOrElse(scrut, matcher, scrutSym.info, if (isFullyDefined(pt)) pt else NoType, hasDefault) + val expr = pmgen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) if (toHoist isEmpty) expr else Block(toHoist, expr) } @@ -1248,8 +1263,6 @@ defined class Foo */ 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 @@ -1328,9 +1341,10 @@ defined class Foo */ // 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 runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + def flatMap(prev: Tree, b: Symbol, next: Tree): Tree def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, nextBinderTp: Type, next: Tree): Tree + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, 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 @@ -1359,7 +1373,8 @@ defined class Foo */ 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) + def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree + = genTypeApply(matchingStrategy DOT vpmName.runOrElse, scrutSym.info, resTp) APPLY (scrut) APPLY (fun(scrutSym, 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 @@ -1368,13 +1383,14 @@ defined class Foo */ 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 flatMap(prev: Tree, b: Symbol, next: Tree): Tree = (prev DOT vpmName.flatMap)(fun(b, next)) def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (genTypeApply(thisCase DOT vpmName.orElse, pt)) APPLY (elseCase) // 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)) + nextBinderTp: Type, next: Tree): Tree = flatMap(guard(cond, res, nextBinderTp), nextBinder, next) def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, UnitClass.tpe), UnitClass.tpe, next) + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = throw new UnsupportedOperationException("Can't optimize under user-defined monad.") } // when we know we're targetting Option, do some inlining the optimizer won't do @@ -1394,18 +1410,17 @@ 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 + override 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)) ) } @@ -1424,20 +1439,16 @@ defined class Foo */ // 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") - 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 - - 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) + override 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( + VAL(prevSym) === prev, + IF (prevSym DOT isEmpty) THEN zero ELSE typedSubst(next, List(b), List(prevSym 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) + ) } override def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = { @@ -1453,6 +1464,13 @@ defined class Foo */ next ) ELSE zero + override def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = + IF (cond) THEN BLOCK( + condSym === TRUE, + nextBinder === res, + next + ) ELSE zero + override def flatMapGuard(guardTree: Tree, next: Tree): Tree = IF (guardTree) THEN next ELSE zero } -- cgit v1.2.3 From 03f00fe232c35189682341e39fac487ed2a70a8c Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 28 Dec 2011 19:06:49 +0100 Subject: [vpm] __match determines match semantics; virtualization determine match strategy by typing `__match` factored out the interface to generate code in this monad, cleaned up codegen a bit no longer solving a context bound to determine the match strategy and the monad's type constructor it's too expensive don't consider implicits looking for __match implicit search causes HUGE slowdowns -- now the overhead is about 4% compared to just assuming there's no __match in scope to support virtualization&staging, we use the type of `__match.one` as the prototype for how to wrap "pure" types and types "in the monad" pure types T are wrapped as P[T], and T goes into the monad as M[T], if one is defined as: def one[T](x: P[T]): M[T] for staging, P will typically be the Rep type constructor, and type M[T] = Rep[Option[T]] furthermore, naive codegen no longer supplies type information -- type inference will have to work it out optimized codegen still does, of course, and that's enough since we only bootstrap that way TODO: improve the test (currently the condition is not represented) --- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 416 ++++++++++----------- src/library/scala/MatchingStrategy.scala | 27 -- test/files/run/virtpatmat_staging.check | 1 + test/files/run/virtpatmat_staging.flags | 1 + test/files/run/virtpatmat_staging.scala | 52 +++ 5 files changed, 252 insertions(+), 245 deletions(-) delete mode 100644 src/library/scala/MatchingStrategy.scala create mode 100644 test/files/run/virtpatmat_staging.check create mode 100644 test/files/run/virtpatmat_staging.flags create mode 100644 test/files/run/virtpatmat_staging.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index 49786813e8..aef85206fa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala @@ -16,26 +16,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: - - 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 */ @@ -43,26 +33,12 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => import global._ import definitions._ - class MatchTranslator(typer: Typer) extends MatchCodeGen { + class MatchTranslator(val 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 import typer._ import typeDebug.{ ptTree, ptBlock, ptLine } - 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 - - context.undetparams = param :: context.undetparams - val result = inferImplicit(EmptyTree, pt, false, false, context) - context.undetparams = savedUndets - - (result.tree, result.subst.to(result.subst.from indexOf param)) - } - - lazy val (matchingStrategy, matchingMonadType) = solveContextBound(MatchingStrategyClass.typeConstructor) /** 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. @@ -72,7 +48,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 @@ -82,7 +58,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => 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) @@ -260,7 +236,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 @@ -373,34 +349,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 @@ -421,7 +395,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: @@ -433,15 +409,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 = { @@ -450,31 +426,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. - -: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) { @@ -489,7 +454,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) } @@ -518,12 +483,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 = @@ -549,10 +509,10 @@ defined class Foo */ // 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 + 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 - pmgen.and(cond, outerCheck) + codegen.and(cond, outerCheck) } else cond @@ -560,7 +520,7 @@ defined class Foo */ // 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)) + def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(codegen._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. @@ -592,7 +552,7 @@ defined class Foo */ // 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) + = codegen._equals(REF(sym), patBinder) AND codegen._isInstanceOf(patBinder, pt.widen) def isRefTp(tp: Type) = tp <:< AnyRefClass.tpe @@ -606,7 +566,7 @@ defined class Foo */ 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 ConstantType(const) => codegen._equals(Literal(const), patBinder) case _ if isMatchUnlessNull => maybeWithOuterCheck(patBinder, pt)(REF(patBinder) OBJ_NE NULL) case _ => typeTest(patBinder, pt) } @@ -639,11 +599,92 @@ defined class Foo */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // the making of the trees /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** 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] - trait TreeMakers { - def inMatchMonad(tp: Type): Type = appliedType(matchingMonadType, List(tp)) - lazy val optimizingCodeGen = matchingMonadType.typeSymbol eq OptionClass + 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 { import CODE._ + val typer: Typer + import typer._ + + 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("") + val runOrElse = newTermName("runOrElse") + val zero = newTermName("zero") + val __match = newTermName("__match") + + def counted(str: String, i: Int) = newTermName(str+i) + } + + final lazy val matchStrategy = // typing `__match` instead of just returning EmptyTree adds 4% to quick.comp.timer + newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { + case SilentResultValue(ms) => ms + case _ => EmptyTree + } + + final def optimizingCodeGen: Boolean = matchStrategy eq EmptyTree + + def __match(n: Name): SelectStart = matchStrategy DOT n + + private lazy val oneSig: Type = + typed(__match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message + + final def inMatchMonad(tp: Type): Type = + if(optimizingCodeGen) optionType(tp) + else appliedType(oneSig, List(tp)).finalResultType + + private lazy val matchMonadSym = + if(optimizingCodeGen) OptionClass + else oneSig.finalResultType.typeSymbol + + final def matchMonadResult(tp: Type): Type = + tp.baseType(matchMonadSym).typeArgs match { + case arg :: Nil => arg + case _ => ErrorType + } + + final def pureType(tp: Type): Type = + if(optimizingCodeGen) tp + else appliedType(oneSig, List(tp)).paramTypes.head + } + trait TreeMakers extends MatchMonadInterface { 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) @@ -675,7 +716,7 @@ 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(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker { @@ -687,20 +728,18 @@ defined class Foo */ val nextBinder: Symbol } - 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))) - } - - abstract class CondTreeMaker extends FreshFunTreeMaker { val cond: Tree val res: Tree + lazy val nextBinder = freshSym(pos, nextBinderTp) + lazy val localSubstitution = Substitution(List(prevBinder), List(CODE.REF(nextBinder))) + def chainBefore(next: Tree, pt: Type): Tree = - atPos(pos)(pmgen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) + atPos(pos)(codegen.flatMapCond(cond, res, nextBinder, nextBinderTp, substitution(next))) } /** @@ -712,10 +751,10 @@ defined class Foo */ */ case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker { def chainBefore(next: Tree, pt: Type): Tree = { - val condAndNext = extraCond map (pmgen.condOptimized(_, next)) getOrElse next + val condAndNext = extraCond map (codegen.condOptimized(_, next)) getOrElse next atPos(extractor.pos)( - if (extractorReturnsBoolean) pmgen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext)) - else pmgen.flatMap(extractor, nextBinder, substitution(condAndNext)) + if (extractorReturnsBoolean) codegen.flatMapCond(extractor, CODE.UNIT, nextBinder, nextBinder.info.widen, substitution(condAndNext)) + else codegen.flatMap(extractor, nextBinder, substitution(condAndNext)) ) } @@ -727,7 +766,7 @@ defined class Foo */ def chainBefore(next: Tree, pt: Type): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL val cond = extraCond map (nullCheck AND _) getOrElse nullCheck - pmgen.condOptimized(cond, substitution(next)) + codegen.condOptimized(cond, substitution(next)) } override def toString = "P"+(prevBinder, extraCond getOrElse "", localSubstitution) @@ -737,7 +776,7 @@ defined class Foo */ // 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) } @@ -746,7 +785,7 @@ defined class Foo */ val nextBinderTp = glb(List(patBinder.info.widen, pt)) val cond = typeAndEqualityTest(patBinder, pt) - val res = pmgen._asInstanceOf(patBinder, nextBinderTp) + val res = codegen._asInstanceOf(patBinder, nextBinderTp) override def toString = "TET"+(patBinder, pt) } @@ -756,7 +795,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) } @@ -791,7 +830,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)) @@ -803,7 +842,7 @@ defined class Foo */ ) BLOCK( VAL(rest) === Function(Nil, substitution(next)), - combinedAlts reduceLeft pmgen.typedOrElse(pt) + combinedAlts reduceLeft codegen.typedOrElse(pt) ) } ) @@ -812,7 +851,7 @@ 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 +")" } @@ -1066,12 +1105,12 @@ defined class Foo */ 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) === pmgen.mkZero(b.info) } + List(storedCond, nextBinder) map { b => VAL(b) === codegen.mkZero(b.info) } } // TODO: finer-grained duplication def chainBefore(next: Tree, pt: Type): Tree = - atPos(pos)(pmgen.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) + atPos(pos)(codegenOpt.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) } case class ReusingCondTreeMaker(sharedPrefix: List[Test], toReused: TreeMaker => TreeMaker) extends TreeMaker { import CODE._ @@ -1093,7 +1132,7 @@ defined class Foo */ 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 + ) ELSE codegen.zero } } @@ -1242,7 +1281,7 @@ defined class Foo */ else casesUnOpt val combinedCases = - cases.map(combineExtractors(_, pt)).reduceLeft(pmgen.typedOrElse(optPt)) + cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) toHoist = ( for (treeMakers <- cases) @@ -1250,9 +1289,9 @@ defined class Foo */ ).flatten.flatten.toList (combinedCases, hasDefault) - } else (pmgen.zero, false) + } else (codegen.zero, false) - val expr = pmgen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) + val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) if (toHoist isEmpty) expr else Block(toHoist, expr) } @@ -1333,30 +1372,42 @@ defined class Foo */ } - 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 + 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 flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, 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 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 + } + + trait AbsOptimizedCodeGen extends AbsCodeGen { + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree } - def pmgen: AbsCodeGen + def codegen: AbsCodeGen + def codegenOpt: AbsOptimizedCodeGen = codegen.asInstanceOf[AbsOptimizedCodeGen] + def typed(tree: Tree, mode: Int, pt: Type): Tree // implemented in MatchTranslator } @@ -1365,40 +1416,38 @@ defined class Foo */ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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 {}) + lazy val codegen: AbsCodeGen = if (optimizingCodeGen) new OptimizedCodeGen else new NaiveCodeGen import CODE._ - trait MatchingStrategyGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => - // methods in MatchingStrategy (the monad companion) -- used directly in translation + class NaiveCodeGen extends CommonCodeGen { + //// 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 - = genTypeApply(matchingStrategy DOT vpmName.runOrElse, scrutSym.info, resTp) APPLY (scrut) APPLY (fun(scrutSym, 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 MonadInstGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => - // methods in the monad instance -- used directly in translation - def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = (prev DOT vpmName.flatMap)(fun(b, next)) - def typedOrElse(pt: Type)(thisCase: Tree, elseCase: Tree): Tree = (genTypeApply(thisCase DOT vpmName.orElse, pt)) APPLY (elseCase) - - // 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), nextBinder, next) - def flatMapGuard(guardTree: Tree, next: Tree): Tree = flatMapCond(guardTree, CODE.UNIT, freshSym(guardTree.pos, UnitClass.tpe), UnitClass.tpe, next) - def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = throw new UnsupportedOperationException("Can't optimize under user-defined monad.") + = __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) } // 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 + class OptimizedCodeGen extends CommonCodeGen with AbsOptimizedCodeGen { lazy val zeroSym = freshSym(NoPosition, optionType(NothingClass.tpe), "zero") /** Inline runOrElse and get rid of Option allocations @@ -1410,7 +1459,7 @@ 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, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = { + 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( @@ -1425,7 +1474,7 @@ defined class Foo */ } // 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) @@ -1434,12 +1483,9 @@ 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(prev: Tree, b: Symbol, next: Tree): Tree = { + 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 @@ -1451,27 +1497,27 @@ defined class Foo */ ) } - 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 flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = + def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = IF (cond) THEN BLOCK( condSym === TRUE, nextBinder === res, next ) ELSE zero - override def flatMapGuard(guardTree: Tree, next: Tree): Tree = + def flatMapGuard(guardTree: Tree, next: Tree): Tree = IF (guardTree) THEN next ELSE zero } @@ -1514,25 +1560,10 @@ defined class Foo */ case _ => tp } - 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("") - val runOrElse = newTermName("runOrElse") - val zero = newTermName("zero") - - def counted(str: String, i: Int) = newTermName(str+i) - } - def typesConform(tp: Type, pt: Type) = ((tp eq pt) || (tp <:< pt)) - trait CommonCodeGen extends AbsCodeGen { self: CommonCodeGen with MatchingStrategyGen with MonadInstGen => + abstract class CommonCodeGen extends AbsCodeGen { 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 @@ -1575,56 +1606,5 @@ defined class Foo */ } } } - - 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/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/test/files/run/virtpatmat_staging.check b/test/files/run/virtpatmat_staging.check new file mode 100644 index 0000000000..106ae40b99 --- /dev/null +++ b/test/files/run/virtpatmat_staging.check @@ -0,0 +1 @@ +runOrElse(7, ?guard(false,?).flatMap(? =>one(foo)).orElse(one(bar))) diff --git a/test/files/run/virtpatmat_staging.flags b/test/files/run/virtpatmat_staging.flags new file mode 100644 index 0000000000..9769db9257 --- /dev/null +++ b/test/files/run/virtpatmat_staging.flags @@ -0,0 +1 @@ + -Yvirtpatmat -Xexperimental diff --git a/test/files/run/virtpatmat_staging.scala b/test/files/run/virtpatmat_staging.scala new file mode 100644 index 0000000000..c17b45043b --- /dev/null +++ b/test/files/run/virtpatmat_staging.scala @@ -0,0 +1,52 @@ +trait Intf { + type Rep[+T] + type M[+T] = Rep[Maybe[T]] + + val __match: Matcher + abstract class Matcher { + // runs the matcher on the given input + def runOrElse[T, U](in: Rep[T])(matcher: Rep[T] => M[U]): Rep[U] + + def zero: M[Nothing] + def one[T](x: Rep[T]): M[T] + def guard[T](cond: Rep[Boolean], then: => Rep[T]): M[T] + def isSuccess[T, U](x: Rep[T])(f: Rep[T] => M[U]): Rep[Boolean] // used for isDefinedAt + } + + abstract class Maybe[+A] { + def flatMap[B](f: Rep[A] => M[B]): M[B] + def orElse[B >: A](alternative: => M[B]): M[B] + } + + implicit def proxyMaybe[A](m: M[A]): Maybe[A] + implicit def repInt(x: Int): Rep[Int] + implicit def repBoolean(x: Boolean): Rep[Boolean] + implicit def repString(x: String): Rep[String] + + def test = 7 match { case 5 => "foo" case _ => "bar" } +} + +trait Impl extends Intf { + type Rep[+T] = String + + object __match extends Matcher { + def runOrElse[T, U](in: Rep[T])(matcher: Rep[T] => M[U]): Rep[U] = ("runOrElse("+ in +", ?" + matcher("?") + ")") + def zero: M[Nothing] = "zero" + def one[T](x: Rep[T]): M[T] = "one("+x.toString+")" + def guard[T](cond: Rep[Boolean], then: => Rep[T]): M[T] = "guard("+cond+","+then+")" + def isSuccess[T, U](x: Rep[T])(f: Rep[T] => M[U]): Rep[Boolean] = ("isSuccess("+x+", ?" + f("?") + ")") + } + + implicit def proxyMaybe[A](m: M[A]): Maybe[A] = new Maybe[A] { + def flatMap[B](f: Rep[A] => M[B]): M[B] = m + ".flatMap(? =>"+ f("?") +")" + def orElse[B >: A](alternative: => M[B]): M[B] = m + ".orElse("+ alternative +")" + } + + def repInt(x: Int): Rep[Int] = x.toString + def repBoolean(x: Boolean): Rep[Boolean] = x.toString + def repString(x: String): Rep[String] = x +} + +object Test extends Impl with Intf with App { + println(test) +} -- cgit v1.2.3 From c58b240177bf6b1017b5fdb6cbfb7be49b4ee3f1 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Sun, 8 Jan 2012 15:39:58 +0100 Subject: [vpm] factored out optimizing codegen --- .../tools/nsc/typechecker/PatMatVirtualiser.scala | 1070 ++++++++++---------- .../scala/tools/nsc/typechecker/Typers.scala | 2 +- 2 files changed, 550 insertions(+), 522 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala index aef85206fa..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. * @@ -33,12 +32,90 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer => import global._ import definitions._ - class MatchTranslator(val 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("") + 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) + } + } + } + + 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 + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// talking to userland +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** 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]`) + - import typer._ - import typeDebug.{ ptTree, ptBlock, ptLine } + * 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 + } + + protected def matchMonadSym: Symbol + } + 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. @@ -56,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, 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) } @@ -497,81 +579,6 @@ class Foo(x: Other) { x._1 } // no error in this order 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(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) - def typeTest(binder: Symbol, pt: Type) = maybeWithOuterCheck(binder, pt)(codegen._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 - = 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) - } - } - /** A conservative approximation of which patterns do not discern anything. * They are discarded during the translation. */ @@ -597,94 +604,70 @@ class Foo(x: Other) { x._1 } // no error in this order } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// the making of the trees +// substitution /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - /** 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 { import CODE._ - val typer: Typer - import typer._ - - 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("") - val runOrElse = newTermName("runOrElse") - val zero = newTermName("zero") - val __match = newTermName("__match") - - def counted(str: String, i: Int) = newTermName(str+i) + 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 } - final lazy val matchStrategy = // typing `__match` instead of just returning EmptyTree adds 4% to quick.comp.timer - newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match { - case SilentResultValue(ms) => ms - case _ => EmptyTree + 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) } - final def optimizingCodeGen: Boolean = matchStrategy eq EmptyTree - - def __match(n: Name): SelectStart = matchStrategy DOT n - - private lazy val oneSig: Type = - typed(__match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message - - final def inMatchMonad(tp: Type): Type = - if(optimizingCodeGen) optionType(tp) - else appliedType(oneSig, List(tp)).finalResultType - private lazy val matchMonadSym = - if(optimizingCodeGen) OptionClass - else oneSig.finalResultType.typeSymbol - - final def matchMonadResult(tp: Type): Type = - tp.baseType(matchMonadSym).typeArgs match { - case arg :: Nil => arg - case _ => ErrorType + // 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(", ", ", ")") + } - final def pureType(tp: Type): Type = - if(optimizingCodeGen) tp - else appliedType(oneSig, List(tp)).paramTypes.head + object EmptySubstitution extends Substitution(Nil, Nil) { + override def apply(tree: Tree): Tree = tree + override def >>(other: Substitution): Substitution = other + } } - trait TreeMakers extends MatchMonadInterface { +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 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) + + 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) @@ -751,7 +734,7 @@ class Foo(x: Other) { x._1 } // no error in this order */ case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol, localSubstitution: Substitution)(extractorReturnsBoolean: Boolean) extends FunTreeMaker { def chainBefore(next: Tree, pt: Type): Tree = { - val condAndNext = extraCond map (codegen.condOptimized(_, next)) getOrElse next + 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)) @@ -766,12 +749,36 @@ class Foo(x: Other) { x._1 } // no error in this order def chainBefore(next: Tree, pt: Type): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL val cond = extraCond map (nullCheck AND _) getOrElse nullCheck - codegen.condOptimized(cond, substitution(next)) + 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 { @@ -784,6 +791,56 @@ class Foo(x: Other) { x._1 } // no error in this order 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 = codegen._asInstanceOf(patBinder, nextBinderTp) override def toString = "TET"+(patBinder, pt) @@ -855,67 +912,292 @@ class Foo(x: Other) { x._1 } // no error in this order override def toString = "G("+ guardTree +")" } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// decisions, decisions -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - object Test { - var currId = 0 - } - case class Test(cond: Cond, treeMaker: TreeMaker) { - // def <:<(other: Test) = cond <:< other.cond - // def andThen_: (prev: List[Test]): List[Test] = - // prev.filterNot(this <:< _) :+ this + def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker]) - private val reusedBy = new collection.mutable.HashSet[Test] - var reuses: Option[Test] = None - def registerReuseBy(later: Test): Unit = { - assert(later.reuses.isEmpty, later.reuses) - reusedBy += later - later.reuses = Some(this) + // 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 } - - val id = { Test.currId += 1; Test.currId} - override def toString = - if (cond eq Top) "T" - else if(cond eq Havoc) "!?" - else "T"+ id + (if(reusedBy nonEmpty) "!["+ treeMaker +"]" else (if(reuses.isEmpty) "["+ treeMaker +"]" else " cf. T"+reuses.get.id)) - } - - object Cond { - // def refines(self: Cond, other: Cond): Boolean = (self, other) match { - // case (Bottom, _) => true - // case (Havoc , _) => true - // case (_ , Top) => true - // case (_ , _) => false - // } - var currId = 0 + removeSubstOnly(treeMakers) } - abstract class Cond { - // def testedPath: Tree - // def <:<(other: Cond) = Cond.refines(this, other) + // 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 - val id = { Cond.currId += 1; Cond.currId} - } + 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 - // does not contribute any knowledge - case object Top extends Cond + // 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] + } - // takes away knowledge. e.g., a user-defined guard - case object Havoc extends Cond + val (cases, toHoist) = optimizeCases(scrutSym, casesUnOpt, pt) - // we know everything! everything! - // this either means the case is unreachable, - // or that it is statically known to be picked -- at this point in the decision tree --> no point in emitting further alternatives - // case object Bottom extends Cond + val combinedCases = + cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) + (combinedCases, hasDefault, toHoist) + } else (codegen.zero, false, Nil) - object EqualityCond { - private val uniques = new collection.mutable.HashMap[(Tree, Tree), EqualityCond] - def apply(testedPath: Tree, rhs: Tree): EqualityCond = uniques getOrElseUpdate((testedPath, rhs), new EqualityCond(testedPath, rhs)) + val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault) + if (toHoist isEmpty) expr + else Block(toHoist, expr) + } } - class EqualityCond(testedPath: Tree, rhs: Tree) extends Cond { + + // 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 + } + case class Test(cond: Cond, treeMaker: TreeMaker) { + // def <:<(other: Test) = cond <:< other.cond + // def andThen_: (prev: List[Test]): List[Test] = + // prev.filterNot(this <:< _) :+ this + + private val reusedBy = new collection.mutable.HashSet[Test] + var reuses: Option[Test] = None + def registerReuseBy(later: Test): Unit = { + assert(later.reuses.isEmpty, later.reuses) + reusedBy += later + later.reuses = Some(this) + } + + val id = { Test.currId += 1; Test.currId} + override def toString = + if (cond eq Top) "T" + else if(cond eq Havoc) "!?" + else "T"+ id + (if(reusedBy nonEmpty) "!["+ treeMaker +"]" else (if(reuses.isEmpty) "["+ treeMaker +"]" else " cf. T"+reuses.get.id)) + } + + object Cond { + // def refines(self: Cond, other: Cond): Boolean = (self, other) match { + // case (Bottom, _) => true + // case (Havoc , _) => true + // case (_ , Top) => true + // case (_ , _) => false + // } + var currId = 0 + } + + abstract class Cond { + // def testedPath: Tree + // def <:<(other: Cond) = Cond.refines(this, other) + + val id = { Cond.currId += 1; Cond.currId} + } + + // does not contribute any knowledge + case object Top extends Cond + + // takes away knowledge. e.g., a user-defined guard + case object Havoc extends Cond + + // we know everything! everything! + // this either means the case is unreachable, + // or that it is statically known to be picked -- at this point in the decision tree --> no point in emitting further alternatives + // case object Bottom extends Cond + + + object EqualityCond { + private val uniques = new collection.mutable.HashMap[(Tree, Tree), EqualityCond] + def apply(testedPath: Tree, rhs: Tree): EqualityCond = uniques getOrElseUpdate((testedPath, rhs), new EqualityCond(testedPath, rhs)) + } + class EqualityCond(testedPath: Tree, rhs: Tree) extends Cond { // def negation = TopCond // inequality doesn't teach us anything // do simplification when we know enough about the tree statically: // - collapse equal trees @@ -951,27 +1233,16 @@ class Foo(x: Other) { x._1 } // no error in this order override def toString = testedPath +" (<: && ==) "+ pt +"#"+ id } -//// CSE - - /** 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] @@ -1032,7 +1303,23 @@ class Foo(x: Other) { x._1 } // no error in this order }, 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]] @@ -1109,8 +1396,8 @@ class Foo(x: Other) { x._1 } // no error in this order } // TODO: finer-grained duplication - def chainBefore(next: Tree, pt: Type): Tree = - atPos(pos)(codegenOpt.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) + 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._ @@ -1135,10 +1422,11 @@ class Foo(x: Other) { x._1 } // no error in this order ) ELSE codegen.zero } } + } -//// DCE - + //// 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) @@ -1147,10 +1435,10 @@ class Foo(x: Other) { x._1 } // no error in this order // do minimal DCE cases } + } - -//// SWITCHES - + //// 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 @@ -1167,7 +1455,7 @@ class Foo(x: Other) { x._1 } // no error in this order private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe) - def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = if (!optimizingCodeGen) None else { + 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) @@ -1208,7 +1496,7 @@ class Foo(x: Other) { x._1 } // no error in this order } if (!isSwitchableTpe(scrut.tpe)) - None + 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._ @@ -1238,216 +1526,27 @@ class Foo(x: Other) { x._1 } // no error in this order } } } - - def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = - doCSE(prevBinder, doDCE(prevBinder, cases, pt), pt) - - - 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{ - 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] - } - - val cases = - if (optimizingCodeGen) optimizeCases(scrutSym, casesUnOpt, pt) - else casesUnOpt - - val combinedCases = - cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt)) - - toHoist = ( - for (treeMakers <- cases) - yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist} - ).flatten.flatten.toList - - (combinedCases, hasDefault) - } else (codegen.zero, false) - - 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() - } - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// 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 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 { - 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 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 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 - } - - trait AbsOptimizedCodeGen extends AbsCodeGen { - def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree - } - - def codegen: AbsCodeGen - def codegenOpt: AbsOptimizedCodeGen = codegen.asInstanceOf[AbsOptimizedCodeGen] - - def typed(tree: Tree, mode: Int, pt: Type): Tree // implemented in MatchTranslator } -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// generate actual trees -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - trait MatchCodeGen extends TreeMakers { - lazy val codegen: AbsCodeGen = if (optimizingCodeGen) new OptimizedCodeGen else new NaiveCodeGen - - import CODE._ + trait OptimizedMatchMonadInterface extends MatchMonadInterface { + override def inMatchMonad(tp: Type): Type = optionType(tp) + override def pureType(tp: Type): Type = tp + override protected def matchMonadSym = OptionClass + } - class NaiveCodeGen extends CommonCodeGen { - //// 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) + trait OptimizedCodegen extends CodegenCore with TypedSubstitution with OptimizedMatchMonadInterface { + override def codegen: AbsCodegen = optimizedCodegen - //// 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) - } + // trait AbsOptimizedCodegen extends AbsCodegen { + // def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree + // } + // def optimizedCodegen: AbsOptimizedCodegen // when we know we're targetting Option, do some inlining the optimizer won't do // for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard // this is a special instance of the advanced inlining optimization that takes a method call on // an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases - class OptimizedCodeGen extends CommonCodeGen with AbsOptimizedCodeGen { + 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 @@ -1493,7 +1592,7 @@ class Foo(x: Other) { x._1 } // no error in this order BLOCK( VAL(prevSym) === prev, - IF (prevSym DOT isEmpty) THEN zero ELSE typedSubst(next, List(b), List(prevSym 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) + 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) ) } @@ -1520,91 +1619,20 @@ class Foo(x: Other) { x._1 } // no error in this order 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 typesConform(tp: Type, pt: Type) = ((tp eq pt) || (tp <:< pt)) - - abstract class CommonCodeGen extends AbsCodeGen { - 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) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d3ff331f98..84f1d1ed6f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3297,7 +3297,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) -- cgit v1.2.3 From 52e7fdc7efaa4ecfd68f71a51375e1870cc78dcc Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 5 Jan 2012 23:33:17 -0800 Subject: Misc optimizations with zip. --- src/compiler/scala/reflect/internal/Types.scala | 2 +- src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala | 3 +-- src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 4 +--- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 371fb8d585..4e842c05da 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -5687,7 +5687,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/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 4a104857db..4012d08e42 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/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index a6c2f75d5e..fa4664b34f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -924,9 +924,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) -- cgit v1.2.3 From f54c2758091c1988ea5e44a3ccbc3b7b9fdddad7 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 2 Feb 2012 20:48:50 -0800 Subject: Cleanups in classfile parser symbol creation. --- .../nsc/symtab/classfile/ClassfileParser.scala | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index ac6dca4422..9c0670e981 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) { -- cgit v1.2.3 From a1e2a94da3002af88c9e5cdb56de3f8da9b8023a Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 2 Feb 2012 20:34:31 -0800 Subject: Updated Flags toString/documentation. In a stunningly unusual demonstration of farsightedness, I was able to generate these changes only by running: scala scala.tools.nsc.util.FlagsUtilCompiler With this much time in between runs: -// Generated by mkFlagsTable() at Mon Oct 11 10:01:09 PDT 2010 +// Generated by mkFlagsTable() at Thu Feb 02 20:31:52 PST 2012 --- src/compiler/scala/reflect/internal/Flags.scala | 36 ++++++++++++------------- test/files/buildmanager/t2562/t2562.check | 10 +++---- test/files/buildmanager/t2649/t2649.check | 4 +-- test/files/buildmanager/t2651_4/t2651_4.check | 4 +-- test/files/buildmanager/t2652/t2652.check | 4 +-- test/files/buildmanager/t2653/t2653.check | 2 +- test/files/buildmanager/t2655/t2655.check | 4 +-- test/files/buildmanager/t2657/t2657.check | 4 +-- test/files/buildmanager/t2789/t2789.check | 4 +-- 9 files changed, 36 insertions(+), 36 deletions(-) (limited to 'src/compiler') 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 => "" // (1L << 12) case PARAM => "" // (1L << 13) case PACKAGE => "" // (1L << 14) - case MACRO => "macro" // (1L << 15) + case MACRO => "" // (1L << 15) case BYNAMEPARAM => "" // (1L << 16) case CONTRAVARIANT => "" // (1L << 17) case ABSOVERRIDE => "absoverride" // (1L << 18) @@ -383,13 +383,13 @@ class Flags extends ModifierFlags { case DEFAULTINIT => "" // (1L << 41) case VBRIDGE => "" // (1L << 42) case VARARGS => "" // (1L << 43) - case 0x100000000000L => "" // (1L << 44) + case TRIEDCOOKING => "" // (1L << 44) case 0x200000000000L => "" // (1L << 45) case 0x400000000000L => "" // (1L << 46) case 0x800000000000L => "" // (1L << 47) case 0x1000000000000L => "" // (1L << 48) - // case `latePRIVATE` => "" // (1L << 49) - // case `lateABSTRACT` => "" // (1L << 50) + case 0x2000000000000L => "" // (1L << 49) + case 0x4000000000000L => "" // (1L << 50) case `lateDEFERRED` => "" // (1L << 51) case `lateFINAL` => "" // (1L << 52) case `lateMETHOD` => "" // (1L << 53) @@ -398,10 +398,10 @@ class Flags extends ModifierFlags { case `notPROTECTED` => "" // (1L << 56) case `notOVERRIDE` => "" // (1L << 57) case `notPRIVATE` => "" // (1L << 58) - // case `notABSTRACT` => "" // (1L << 59) - // case `notDEFERRED` => "" // (1L << 60) - // case `notFINAL` => "" // (1L << 61) - // case `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/test/files/buildmanager/t2562/t2562.check b/test/files/buildmanager/t2562/t2562.check index 390bbb9986..74575f28ea 100644 --- a/test/files/buildmanager/t2562/t2562.check +++ b/test/files/buildmanager/t2562/t2562.check @@ -3,10 +3,10 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(object A -> List(Changed(Definition(A.x3))[method x3 changed from ()Int to ()String flags: ])) -invalidate B.scala because it references changed definition [Changed(Definition(A.x3))[method x3 changed from ()Int to ()String flags: ]] +Changes: Map(object A -> List(Changed(Definition(A.x3))[method x3 changed from ()Int to ()String flags: ])) +invalidate B.scala because it references changed definition [Changed(Definition(A.x3))[method x3 changed from ()Int to ()String flags: ]] compiling Set(B.scala) -Changes: Map(object B -> List(Changed(Definition(B.x2))[method x2 changed from ()Int to ()String flags: ])) -invalidate A.scala because it references changed definition [Changed(Definition(B.x2))[method x2 changed from ()Int to ()String flags: ]] +Changes: Map(object B -> List(Changed(Definition(B.x2))[method x2 changed from ()Int to ()String flags: ])) +invalidate A.scala because it references changed definition [Changed(Definition(B.x2))[method x2 changed from ()Int to ()String flags: ]] compiling Set(A.scala, B.scala) -Changes: Map(object A -> List(Changed(Definition(A.x0))[method x0 changed from ()Int to ()String flags: ], Changed(Definition(A.x1))[method x1 changed from ()Int to ()String flags: ], Changed(Definition(A.x2))[method x2 changed from ()Int to ()String flags: ]), object B -> List(Changed(Definition(B.x0))[method x0 changed from ()Int to ()String flags: ], Changed(Definition(B.x1))[method x1 changed from ()Int to ()String flags: ])) +Changes: Map(object A -> List(Changed(Definition(A.x0))[method x0 changed from ()Int to ()String flags: ], Changed(Definition(A.x1))[method x1 changed from ()Int to ()String flags: ], Changed(Definition(A.x2))[method x2 changed from ()Int to ()String flags: ]), object B -> List(Changed(Definition(B.x0))[method x0 changed from ()Int to ()String flags: ], Changed(Definition(B.x1))[method x1 changed from ()Int to ()String flags: ])) diff --git a/test/files/buildmanager/t2649/t2649.check b/test/files/buildmanager/t2649/t2649.check index 390f284fd3..d0f41f32ec 100644 --- a/test/files/buildmanager/t2649/t2649.check +++ b/test/files/buildmanager/t2649/t2649.check @@ -3,7 +3,7 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(object A -> List(Changed(Definition(A.x))[method x changed from (zz: Int, yy: Int)Int to (yy: Int, zz: Int)Int flags: ])) -invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from (zz: Int, yy: Int)Int to (yy: Int, zz: Int)Int flags: ]] +Changes: Map(object A -> List(Changed(Definition(A.x))[method x changed from (zz: Int, yy: Int)Int to (yy: Int, zz: Int)Int flags: ])) +invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from (zz: Int, yy: Int)Int to (yy: Int, zz: Int)Int flags: ]] compiling Set(B.scala) Changes: Map(object B -> List()) diff --git a/test/files/buildmanager/t2651_4/t2651_4.check b/test/files/buildmanager/t2651_4/t2651_4.check index c4ce382b5f..b182f31c09 100644 --- a/test/files/buildmanager/t2651_4/t2651_4.check +++ b/test/files/buildmanager/t2651_4/t2651_4.check @@ -3,8 +3,8 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(trait A -> List(Changed(Definition(A.x))[method x changed from ()T to ()T flags: ], Changed(Definition(A.y))[method y changed from (a: T)Unit to (a: T)Unit flags: ], Changed(Definition(A.z))[method z changed from [B <: T]()Unit to [B <: T]()Unit flags: ])) -invalidate B.scala because inherited method changed [Changed(Definition(A.x))[method x changed from ()T to ()T flags: ]] +Changes: Map(trait A -> List(Changed(Definition(A.x))[method x changed from ()T to ()T flags: ], Changed(Definition(A.y))[method y changed from (a: T)Unit to (a: T)Unit flags: ], Changed(Definition(A.z))[method z changed from [B <: T]()Unit to [B <: T]()Unit flags: ])) +invalidate B.scala because inherited method changed [Changed(Definition(A.x))[method x changed from ()T to ()T flags: ]] compiling Set(B.scala) B.scala:2: error: type mismatch; found : Int(3) diff --git a/test/files/buildmanager/t2652/t2652.check b/test/files/buildmanager/t2652/t2652.check index f517f9e95a..071281c6ff 100644 --- a/test/files/buildmanager/t2652/t2652.check +++ b/test/files/buildmanager/t2652/t2652.check @@ -3,7 +3,7 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(class A -> List(Added(Definition(A.x$mBc$sp)), Added(Definition(A.x$mCc$sp)), Added(Definition(A.x$mDc$sp)), Added(Definition(A.x$mFc$sp)), Added(Definition(A.x$mIc$sp)), Added(Definition(A.x$mJc$sp)), Added(Definition(A.x$mSc$sp)), Added(Definition(A.x$mVc$sp)), Added(Definition(A.x$mZc$sp)), Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: ])) -invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: ]] +Changes: Map(class A -> List(Added(Definition(A.x$mBc$sp)), Added(Definition(A.x$mCc$sp)), Added(Definition(A.x$mDc$sp)), Added(Definition(A.x$mFc$sp)), Added(Definition(A.x$mIc$sp)), Added(Definition(A.x$mJc$sp)), Added(Definition(A.x$mSc$sp)), Added(Definition(A.x$mVc$sp)), Added(Definition(A.x$mZc$sp)), Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: ])) +invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from [T](t: T)T to [T](t: T)T flags: ]] compiling Set(B.scala) Changes: Map(object B -> List()) diff --git a/test/files/buildmanager/t2653/t2653.check b/test/files/buildmanager/t2653/t2653.check index 6a4fc0e982..36781522af 100644 --- a/test/files/buildmanager/t2653/t2653.check +++ b/test/files/buildmanager/t2653/t2653.check @@ -3,7 +3,7 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(class A -> List(Changed(Class(A))[ tparams: List((type T,type T))], Changed(Definition(A.))[constructor A changed from ()A[T] to ()A[T] flags: ])) +Changes: Map(class A -> List(Changed(Class(A))[ tparams: List((type T,type T))], Changed(Definition(A.))[constructor A changed from ()A[T] to ()A[T] flags: ])) invalidate B.scala because it references changed class [Changed(Class(A))[ tparams: List((type T,type T))]] compiling Set(B.scala) B.scala:2: error: type mismatch; diff --git a/test/files/buildmanager/t2655/t2655.check b/test/files/buildmanager/t2655/t2655.check index c473e9fd6e..41ce65a2f5 100644 --- a/test/files/buildmanager/t2655/t2655.check +++ b/test/files/buildmanager/t2655/t2655.check @@ -3,8 +3,8 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(object A -> List(Changed(Definition(A.x))[method x changed from (i: Function0)Unit to (i: Function0)Unit flags: ])) -invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from (i: Function0)Unit to (i: Function0)Unit flags: ]] +Changes: Map(object A -> List(Changed(Definition(A.x))[method x changed from (i: Function0)Unit to (i: Function0)Unit flags: ])) +invalidate B.scala because it references changed definition [Changed(Definition(A.x))[method x changed from (i: Function0)Unit to (i: Function0)Unit flags: ]] compiling Set(B.scala) B.scala:2: error: type mismatch; found : String("3") diff --git a/test/files/buildmanager/t2657/t2657.check b/test/files/buildmanager/t2657/t2657.check index 3fd0e0666d..74ba87a21d 100644 --- a/test/files/buildmanager/t2657/t2657.check +++ b/test/files/buildmanager/t2657/t2657.check @@ -3,8 +3,8 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(class A -> List(Changed(Definition(A.y))[method y changed from (i: Int)String to (i: Int)String flags: implicit ])) -invalidate B.scala because inherited method changed [Changed(Definition(A.y))[method y changed from (i: Int)String to (i: Int)String flags: implicit ]] +Changes: Map(class A -> List(Changed(Definition(A.y))[method y changed from (i: Int)String to (i: Int)String flags: implicit ])) +invalidate B.scala because inherited method changed [Changed(Definition(A.y))[method y changed from (i: Int)String to (i: Int)String flags: implicit ]] compiling Set(B.scala) B.scala:2: error: type mismatch; found : Int(3) diff --git a/test/files/buildmanager/t2789/t2789.check b/test/files/buildmanager/t2789/t2789.check index 78c5119355..a7c767cc45 100644 --- a/test/files/buildmanager/t2789/t2789.check +++ b/test/files/buildmanager/t2789/t2789.check @@ -3,8 +3,8 @@ compiling Set(A.scala, B.scala) Changes: Map() builder > A.scala compiling Set(A.scala) -Changes: Map(class A -> List(Changed(Definition(A.e))[method e changed from ()E to ()E flags: implicit ]), class E -> List()) -invalidate B.scala because inherited method changed [Changed(Definition(A.e))[method e changed from ()E to ()E flags: implicit ]] +Changes: Map(class A -> List(Changed(Definition(A.e))[method e changed from ()E to ()E flags: implicit ]), class E -> List()) +invalidate B.scala because inherited method changed [Changed(Definition(A.e))[method e changed from ()E to ()E flags: implicit ]] compiling Set(B.scala) B.scala:2: error: could not find implicit value for parameter y: E val y = x(3) -- cgit v1.2.3