From f0d913b51dbe1a098e813aa08197eef9c6cbf737 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 10 Dec 2013 11:04:26 +0100 Subject: SI-8062 Fix inliner cycle with recursion, separate compilation ICodeReaders, which decompiles JVM bytecode to ICode, was not setting the `recursive` attribute of `IMethod`. This meant that the inliner got into a cycle, repeatedly inlining the recursive call. The method name `filter` was needed to trigger this as the inliner heuristically treats that as a more attractive inlining candidate, based on `isMonadicMethod`. This commit: - refactors the checking / setting of `virtual` - adds this to ICodeReaders - tests the case involving `invokevirtual` I'm not sure how to setup a test that fails without the other changes to `ICodeReader` (for invokestatic and invokespecial). --- src/compiler/scala/tools/nsc/backend/icode/GenICode.scala | 5 +---- src/compiler/scala/tools/nsc/backend/icode/Members.scala | 4 ++++ src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala | 7 ++++++- test/files/pos/t8062.flags | 1 + test/files/pos/t8062/A_1.scala | 5 +++++ test/files/pos/t8062/B_2.scala | 3 +++ 6 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 test/files/pos/t8062.flags create mode 100644 test/files/pos/t8062/A_1.scala create mode 100644 test/files/pos/t8062/B_2.scala diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 44d7a1929b..71a5b85271 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -954,10 +954,7 @@ abstract class GenICode extends SubComponent { case _ => } ctx1.bb.emit(cm, tree.pos) - - if (sym == ctx1.method.symbol) { - ctx1.method.recursive = true - } + ctx1.method.updateRecursive(sym) generatedType = if (sym.isClassConstructor) UNIT else toTypeKind(sym.info.resultType); diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index b74770f051..00bcf603cf 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -185,6 +185,10 @@ trait Members { this } + final def updateRecursive(called: Symbol): Unit = { + recursive ||= (called == symbol) + } + def addLocal(l: Local): Local = findOrElse(locals)(_ == l) { locals ::= l ; l } def addParam(p: Local): Unit = diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index c304c18c4f..d0c540a2c6 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -489,23 +489,28 @@ abstract class ICodeReader extends ClassfileParser { case JVM.invokevirtual => val m = pool.getMemberSymbol(in.nextChar, false); size += 2 code.emit(CALL_METHOD(m, Dynamic)) + method.updateRecursive(m) case JVM.invokeinterface => val m = pool.getMemberSymbol(in.nextChar, false); size += 4 in.skip(2) code.emit(CALL_METHOD(m, Dynamic)) + // invokeinterface can't be recursive case JVM.invokespecial => val m = pool.getMemberSymbol(in.nextChar, false); size += 2 val style = if (m.name == nme.CONSTRUCTOR || m.isPrivate) Static(true) else SuperCall(m.owner.name); code.emit(CALL_METHOD(m, style)) + method.updateRecursive(m) case JVM.invokestatic => val m = pool.getMemberSymbol(in.nextChar, true); size += 2 if (isBox(m)) code.emit(BOX(toTypeKind(m.info.paramTypes.head))) else if (isUnbox(m)) code.emit(UNBOX(toTypeKind(m.info.resultType))) - else + else { code.emit(CALL_METHOD(m, Static(false))) + method.updateRecursive(m) + } case JVM.invokedynamic => // TODO, this is just a place holder. A real implementation must parse the class constant entry debuglog("Found JVM invokedynamic instructionm, inserting place holder ICode INVOKE_DYNAMIC.") diff --git a/test/files/pos/t8062.flags b/test/files/pos/t8062.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/pos/t8062.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/pos/t8062/A_1.scala b/test/files/pos/t8062/A_1.scala new file mode 100644 index 0000000000..ca0411dae8 --- /dev/null +++ b/test/files/pos/t8062/A_1.scala @@ -0,0 +1,5 @@ +package warmup + +object Warmup { + def filter[A](p: Any => Boolean): Any = filter[Any](p) +} diff --git a/test/files/pos/t8062/B_2.scala b/test/files/pos/t8062/B_2.scala new file mode 100644 index 0000000000..f0a6761488 --- /dev/null +++ b/test/files/pos/t8062/B_2.scala @@ -0,0 +1,3 @@ +object Test { + warmup.Warmup.filter[Any](x => false) +} -- cgit v1.2.3 From 47562e7adbf8577789b3432f4bfbb36d786c6b32 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 10 Dec 2013 15:50:20 -0800 Subject: Revert "SI-6426, importable _." This reverts commit d2316df920ffa4804fe51e8f8780240c46efa982. We can't make `_` an illegal identifier -- it's legal in Java, so we must be able to name these Java underscores. --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 7 +++++-- src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 5 +---- test/files/neg/t6426.check | 7 ------- test/files/neg/t6426.scala | 5 ----- 4 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 test/files/neg/t6426.check delete mode 100644 test/files/neg/t6426.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 996287dea8..b9e4109623 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -980,8 +980,11 @@ self => /** Assumed (provisionally) to be TermNames. */ def ident(skipIt: Boolean): Name = - if (isIdent) rawIdent().encode - else { + if (isIdent) { + val name = in.name.encode + in.nextToken() + name + } else { syntaxErrorOrIncomplete(expectedMsg(IDENTIFIER), skipIt) nme.ERROR } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 1aa50be83a..8d295a28d0 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -611,10 +611,7 @@ trait Scanners extends ScannersCommon { if (ch == '`') { nextChar() finishNamed(BACKQUOTED_IDENT) - if (name.length == 0) - syntaxError("empty quoted identifier") - else if (name == nme.WILDCARD) - syntaxError("wildcard invalid as backquoted identifier") + if (name.length == 0) syntaxError("empty quoted identifier") } else syntaxError("unclosed quoted identifier") } diff --git a/test/files/neg/t6426.check b/test/files/neg/t6426.check deleted file mode 100644 index 149f74c4de..0000000000 --- a/test/files/neg/t6426.check +++ /dev/null @@ -1,7 +0,0 @@ -t6426.scala:4: error: wildcard invalid as backquoted identifier - println(`_`.Buffer(0)) - ^ -t6426.scala:5: error: ')' expected but '}' found. -} -^ -two errors found diff --git a/test/files/neg/t6426.scala b/test/files/neg/t6426.scala deleted file mode 100644 index a27d18eb58..0000000000 --- a/test/files/neg/t6426.scala +++ /dev/null @@ -1,5 +0,0 @@ -class A { - import collection.{mutable => _, _} - - println(`_`.Buffer(0)) -} -- cgit v1.2.3 From 2aa9da578e03987427a2d932becc75fc0f016d8b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 11 Dec 2013 15:15:56 -0800 Subject: Partially revert f8d8f7d08d. Need `${dist.dir}/lib/scala-partest.jar` for maven publish. We still don't want to distribute it in the distribution, but will have to remove it in release script, as 2.10.x's build hasn't been refactoreded like master's, and I'm not backporting it. --- build.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/build.xml b/build.xml index 377a0c958b..39685163a6 100644 --- a/build.xml +++ b/build.xml @@ -1837,6 +1837,7 @@ TODO: + -- cgit v1.2.3 From 3fa2c97853de2110227f50982187b4377b8772bc Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 11 Dec 2013 17:05:41 -0800 Subject: Report error on code size overflow, log method name. We used to silently skip class files that would exceed the JVM's size limits. While rare, this should still be an error. While I was at it, also included the name of the offending method. --- src/asm/scala/tools/asm/MethodWriter.java | 7 ++++++- .../scala/tools/nsc/backend/jvm/GenASM.scala | 9 ++++---- test/files/run/large_code.check | 3 +++ test/files/run/large_code.scala | 24 ++++++++++++++++++++++ 4 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 test/files/run/large_code.check create mode 100644 test/files/run/large_code.scala diff --git a/src/asm/scala/tools/asm/MethodWriter.java b/src/asm/scala/tools/asm/MethodWriter.java index 321bacb6fc..887cb28c6f 100644 --- a/src/asm/scala/tools/asm/MethodWriter.java +++ b/src/asm/scala/tools/asm/MethodWriter.java @@ -1853,7 +1853,12 @@ class MethodWriter extends MethodVisitor { int size = 8; if (code.length > 0) { if (code.length > 65536) { - throw new RuntimeException("Method code too large!"); + String nameString = ""; + int i = 0; + // find item that corresponds to the index of our name + while (i < cw.items.length && (cw.items[i] == null || cw.items[i].index != name)) i++; + if (cw.items[i] != null) nameString = cw.items[i].strVal1 +"'s "; + throw new RuntimeException("Method "+ nameString +"code too large!"); } cw.newUTF8("Code"); size += 18 + code.length + 8 * handlerCount; diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 97140aca2e..19cdcd2590 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -450,7 +450,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } // ----------------------------------------------------------------------------------------- - // utitilies useful when emitting plain, mirror, and beaninfo classes. + // utilities useful when emitting plain, mirror, and beaninfo classes. // ----------------------------------------------------------------------------------------- def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) { @@ -458,9 +458,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { val arr = jclass.toByteArray() bytecodeWriter.writeClass(label, jclassName, arr, sym) } catch { - case e: java.lang.RuntimeException if(e.getMessage() == "Class file too large!") => - // TODO check where ASM throws the equivalent of CodeSizeTooBigException - log("Skipped class "+jclassName+" because it exceeds JVM limits (it's too big or has methods that are too long).") + case e: java.lang.RuntimeException if e != null && (e.getMessage contains "too large!") => + reporter.error(sym.pos, + s"Could not write class $jclassName because it exceeds JVM code size limits. ${e.getMessage}") } } @@ -1402,7 +1402,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { addInnerClasses(clasz.symbol, jclass) jclass.visitEnd() writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol) - } /** diff --git a/test/files/run/large_code.check b/test/files/run/large_code.check new file mode 100644 index 0000000000..6ad50967bc --- /dev/null +++ b/test/files/run/large_code.check @@ -0,0 +1,3 @@ +newSource1.scala:1: error: Could not write class BigEnoughToFail because it exceeds JVM code size limits. Method tooLong's code too large! +class BigEnoughToFail { + ^ diff --git a/test/files/run/large_code.scala b/test/files/run/large_code.scala new file mode 100644 index 0000000000..f9d7f8c95b --- /dev/null +++ b/test/files/run/large_code.scala @@ -0,0 +1,24 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +// a cold run of partest takes about 15s for this test on my laptop +object Test extends DirectTest { + override def extraSettings: String = "-usejavacp -d " + testOutput.path + + // test that we hit the code size limit and error out gracefully + // 5958 is the magic number (2^16/11 -- each `a(1,2,3,4,5,6)` is 11 bytes of bytecode) + override def code + = s""" + |class BigEnoughToFail { + | def a(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int): Unit = {} + | def tooLong: Unit = { + | ${(1 to 5958) map (_ => "a(1,2,3,4,5,6)") mkString(";")} + | } + |}""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} -- cgit v1.2.3 From a12dd9c3b6fb4a767eec8f6d3bf0a9a2266eff85 Mon Sep 17 00:00:00 2001 From: Mirco Dotta Date: Tue, 10 Dec 2013 17:12:58 +0100 Subject: Test demonstrating SI-8085 * The presentation compiler sourcepath is now correctly set-up. * Amazingly enough (for me at least), the outer import in the package object seem to be responsible of the faulty behavior. In fact, if you move the import clause *inside* the package object, the test succeed! The test's output does provide the correct hint of this: ``` % diff /Users/mirco/Projects/ide/scala/test/files/presentation/t8085-presentation.log /Users/mirco/Projects/ide/scala/test/files/presentation/t8085.check @@ -1,3 +1,2 @@ reload: NodeScalaSuite.scala -prefixes differ: .nodescala,nodescala -value always is not a member of object scala.concurrent.Future +Test OK ``` Notice the ``-prefixes differ: .nodescala,nodescala``. And compare it with the output when the import clause is placed inside the package object: ``` % diff /Users/mirco/Projects/ide/scala/test/files/presentation/t8085-presentation.log /Users/mirco/Projects/ide/scala/test/files/presentation/t8085.check @@ -1,4 +1,2 @@ reload: NodeScalaSuite.scala -reload: NodeScalaSuite.scala -open package module: package object nodescala Test OK ``` Notice now the ``-open package module: package object nodescala``! --- test/files/presentation/t8085.check | 3 +++ test/files/presentation/t8085.flags | 1 + test/files/presentation/t8085/Test.scala | 28 ++++++++++++++++++++++ .../presentation/t8085/src/nodescala/Foo.scala | 3 +++ .../t8085/src/nodescala/NodeScalaSuite.scala | 12 ++++++++++ .../presentation/t8085/src/nodescala/package.scala | 8 +++++++ 6 files changed, 55 insertions(+) create mode 100644 test/files/presentation/t8085.check create mode 100644 test/files/presentation/t8085.flags create mode 100644 test/files/presentation/t8085/Test.scala create mode 100644 test/files/presentation/t8085/src/nodescala/Foo.scala create mode 100644 test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala create mode 100644 test/files/presentation/t8085/src/nodescala/package.scala diff --git a/test/files/presentation/t8085.check b/test/files/presentation/t8085.check new file mode 100644 index 0000000000..46aac791f5 --- /dev/null +++ b/test/files/presentation/t8085.check @@ -0,0 +1,3 @@ +reload: NodeScalaSuite.scala +prefixes differ: .nodescala,nodescala +value always is not a member of object scala.concurrent.Future diff --git a/test/files/presentation/t8085.flags b/test/files/presentation/t8085.flags new file mode 100644 index 0000000000..ec35b223d8 --- /dev/null +++ b/test/files/presentation/t8085.flags @@ -0,0 +1 @@ +-sourcepath src diff --git a/test/files/presentation/t8085/Test.scala b/test/files/presentation/t8085/Test.scala new file mode 100644 index 0000000000..5d11009818 --- /dev/null +++ b/test/files/presentation/t8085/Test.scala @@ -0,0 +1,28 @@ +import scala.tools.nsc.interactive.tests.InteractiveTest +import scala.reflect.internal.util.SourceFile +import scala.tools.nsc.interactive.Response + +object Test extends InteractiveTest { + + override def execute(): Unit = { + // loadSourceAndWaitUntilTypechecked("package.scala") // <-- uncomment this line and the test succeed + val src = loadSourceAndWaitUntilTypechecked("NodeScalaSuite.scala") + checkErrors(src) + } + + private def loadSourceAndWaitUntilTypechecked(sourceName: String): SourceFile = { + val sourceFile = sourceFiles.find(_.file.name == sourceName).head + askReload(List(sourceFile)).get + askLoadedTyped(sourceFile).get + sourceFile + } + + private def checkErrors(source: SourceFile): Unit = compiler.getUnitOf(source) match { + case Some(unit) => + val problems = unit.problems.toList + if(problems.isEmpty) reporter.println("Test OK") + else problems.foreach(problem => reporter.println(problem.msg)) + + case None => reporter.println("No compilation unit found for " + source.file.name) + } +} diff --git a/test/files/presentation/t8085/src/nodescala/Foo.scala b/test/files/presentation/t8085/src/nodescala/Foo.scala new file mode 100644 index 0000000000..19efdb65dd --- /dev/null +++ b/test/files/presentation/t8085/src/nodescala/Foo.scala @@ -0,0 +1,3 @@ +package nodescala + +class Foo diff --git a/test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala b/test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala new file mode 100644 index 0000000000..a8803ff86d --- /dev/null +++ b/test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala @@ -0,0 +1,12 @@ +package nodescala + +import scala.concurrent.Future + +class NodeScalaSuite { + Future.always(517) + + // This is here only to prove that the presentation compiler is instantiated with the + // correct `sourcepath` value (if it wasn't, you would see a `not found: type Foo` in + // the test's output + println(new Foo()) +} \ No newline at end of file diff --git a/test/files/presentation/t8085/src/nodescala/package.scala b/test/files/presentation/t8085/src/nodescala/package.scala new file mode 100644 index 0000000000..6e9d4b729a --- /dev/null +++ b/test/files/presentation/t8085/src/nodescala/package.scala @@ -0,0 +1,8 @@ +import scala.concurrent.Future // <-- if you move the import *inside* the package object, then it all works fine!! + +package object nodescala { + implicit class FutureCompanionOps[T](val f: Future.type) extends AnyVal { + def always[T](value: T): Future[T] = Promise[T].success(value).future + } +} + -- cgit v1.2.3 From 7e85b595502974bebf2f2625c6bc3645f0d3ab27 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 17 Dec 2013 17:47:15 +0100 Subject: SI-8085 Fix BrowserTraverser for package objects A source file like: import foo.bar package object baz Is parsed into: package { import foo.bar package baz { object `package` } } A special case in Namers compensates by adjusting the owner of `baz` to be ``, rather than ``. This wasn't being accounted for in `BrowserTraverser`, which underpins `-sourcepath`, and allows the presentation compiler to load top level symbols from sources outside those passes as the list of sources to compile. This bug did not appear in sources like: package p1 package object p2 { ... } ... because the parser does not wrap this in the `package {}` This goes some way to explaining why it has gone unnoticed for so long. --- .../scala/tools/nsc/symtab/BrowsingLoaders.scala | 6 +++-- test/files/presentation/t8085.check | 4 ++-- test/files/presentation/t8085/Test.scala | 1 - .../t8085/src/nodescala/NodeScalaSuite.scala | 6 ++--- .../presentation/t8085/src/nodescala/package.scala | 7 +++--- test/files/presentation/t8085b.check | 3 +++ test/files/presentation/t8085b.flags | 1 + test/files/presentation/t8085b/Test.scala | 27 ++++++++++++++++++++++ .../presentation/t8085b/src/p1/nodescala/Foo.scala | 4 ++++ .../t8085b/src/p1/nodescala/NodeScalaSuite.scala | 11 +++++++++ .../t8085b/src/p1/nodescala/package.scala | 9 ++++++++ 11 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 test/files/presentation/t8085b.check create mode 100644 test/files/presentation/t8085b.flags create mode 100644 test/files/presentation/t8085b/Test.scala create mode 100644 test/files/presentation/t8085b/src/p1/nodescala/Foo.scala create mode 100644 test/files/presentation/t8085b/src/p1/nodescala/NodeScalaSuite.scala create mode 100644 test/files/presentation/t8085b/src/p1/nodescala/package.scala diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index f2aab36b51..c7bd678385 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -64,8 +64,10 @@ abstract class BrowsingLoaders extends SymbolLoaders { addPackagePrefix(pre) packagePrefix += ("." + name) case Ident(name) => - if (packagePrefix.length != 0) packagePrefix += "." - packagePrefix += name + if (name != nme.EMPTY_PACKAGE_NAME) { // mirrors logic in Namers, see createPackageSymbol + if (packagePrefix.length != 0) packagePrefix += "." + packagePrefix += name + } case _ => throw new MalformedInput(pkg.pos.point, "illegal tree node in package prefix: "+pkg) } diff --git a/test/files/presentation/t8085.check b/test/files/presentation/t8085.check index 46aac791f5..79c1b2aa17 100644 --- a/test/files/presentation/t8085.check +++ b/test/files/presentation/t8085.check @@ -1,3 +1,3 @@ reload: NodeScalaSuite.scala -prefixes differ: .nodescala,nodescala -value always is not a member of object scala.concurrent.Future +open package module: package nodescala +Test OK diff --git a/test/files/presentation/t8085/Test.scala b/test/files/presentation/t8085/Test.scala index 5d11009818..e46b7ab8c8 100644 --- a/test/files/presentation/t8085/Test.scala +++ b/test/files/presentation/t8085/Test.scala @@ -5,7 +5,6 @@ import scala.tools.nsc.interactive.Response object Test extends InteractiveTest { override def execute(): Unit = { - // loadSourceAndWaitUntilTypechecked("package.scala") // <-- uncomment this line and the test succeed val src = loadSourceAndWaitUntilTypechecked("NodeScalaSuite.scala") checkErrors(src) } diff --git a/test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala b/test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala index a8803ff86d..45e43c7afb 100644 --- a/test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala +++ b/test/files/presentation/t8085/src/nodescala/NodeScalaSuite.scala @@ -1,12 +1,10 @@ package nodescala -import scala.concurrent.Future - class NodeScalaSuite { - Future.always(517) + "".rich // This is here only to prove that the presentation compiler is instantiated with the // correct `sourcepath` value (if it wasn't, you would see a `not found: type Foo` in // the test's output println(new Foo()) -} \ No newline at end of file +} diff --git a/test/files/presentation/t8085/src/nodescala/package.scala b/test/files/presentation/t8085/src/nodescala/package.scala index 6e9d4b729a..26fb9f08e4 100644 --- a/test/files/presentation/t8085/src/nodescala/package.scala +++ b/test/files/presentation/t8085/src/nodescala/package.scala @@ -1,8 +1,7 @@ -import scala.concurrent.Future // <-- if you move the import *inside* the package object, then it all works fine!! +import scala.Some // <-- if you move the import *inside* the package object, then it all works fine!! package object nodescala { - implicit class FutureCompanionOps[T](val f: Future.type) extends AnyVal { - def always[T](value: T): Future[T] = Promise[T].success(value).future + implicit class StringOps(val f: String) { + def rich = 0 } } - diff --git a/test/files/presentation/t8085b.check b/test/files/presentation/t8085b.check new file mode 100644 index 0000000000..79c1b2aa17 --- /dev/null +++ b/test/files/presentation/t8085b.check @@ -0,0 +1,3 @@ +reload: NodeScalaSuite.scala +open package module: package nodescala +Test OK diff --git a/test/files/presentation/t8085b.flags b/test/files/presentation/t8085b.flags new file mode 100644 index 0000000000..ec35b223d8 --- /dev/null +++ b/test/files/presentation/t8085b.flags @@ -0,0 +1 @@ +-sourcepath src diff --git a/test/files/presentation/t8085b/Test.scala b/test/files/presentation/t8085b/Test.scala new file mode 100644 index 0000000000..e46b7ab8c8 --- /dev/null +++ b/test/files/presentation/t8085b/Test.scala @@ -0,0 +1,27 @@ +import scala.tools.nsc.interactive.tests.InteractiveTest +import scala.reflect.internal.util.SourceFile +import scala.tools.nsc.interactive.Response + +object Test extends InteractiveTest { + + override def execute(): Unit = { + val src = loadSourceAndWaitUntilTypechecked("NodeScalaSuite.scala") + checkErrors(src) + } + + private def loadSourceAndWaitUntilTypechecked(sourceName: String): SourceFile = { + val sourceFile = sourceFiles.find(_.file.name == sourceName).head + askReload(List(sourceFile)).get + askLoadedTyped(sourceFile).get + sourceFile + } + + private def checkErrors(source: SourceFile): Unit = compiler.getUnitOf(source) match { + case Some(unit) => + val problems = unit.problems.toList + if(problems.isEmpty) reporter.println("Test OK") + else problems.foreach(problem => reporter.println(problem.msg)) + + case None => reporter.println("No compilation unit found for " + source.file.name) + } +} diff --git a/test/files/presentation/t8085b/src/p1/nodescala/Foo.scala b/test/files/presentation/t8085b/src/p1/nodescala/Foo.scala new file mode 100644 index 0000000000..8ed1ada6b6 --- /dev/null +++ b/test/files/presentation/t8085b/src/p1/nodescala/Foo.scala @@ -0,0 +1,4 @@ +package p1 +package nodescala + +class Foo diff --git a/test/files/presentation/t8085b/src/p1/nodescala/NodeScalaSuite.scala b/test/files/presentation/t8085b/src/p1/nodescala/NodeScalaSuite.scala new file mode 100644 index 0000000000..f6da67bdc7 --- /dev/null +++ b/test/files/presentation/t8085b/src/p1/nodescala/NodeScalaSuite.scala @@ -0,0 +1,11 @@ +package p1 +package nodescala + +class NodeScalaSuite { + "".rich + + // This is here only to prove that the presentation compiler is instantiated with the + // correct `sourcepath` value (if it wasn't, you would see a `not found: type Foo` in + // the test's output + println(new Foo()) +} diff --git a/test/files/presentation/t8085b/src/p1/nodescala/package.scala b/test/files/presentation/t8085b/src/p1/nodescala/package.scala new file mode 100644 index 0000000000..cc383f1bab --- /dev/null +++ b/test/files/presentation/t8085b/src/p1/nodescala/package.scala @@ -0,0 +1,9 @@ +import scala.Some // <-- if you move the import *inside* the package object, then it all works fine!! + +package p1 { + package object nodescala { + implicit class StringOps(val f: String) { + def rich = 0 + } + } +} -- cgit v1.2.3 From c0cb1d891a663d474a50d34cdf097dc3c1a2d7cc Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 28 Dec 2013 02:50:06 +0300 Subject: [nomaster] codifies the state of the art wrt SI-8104 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As it was discovered in SI-8104, whiteboxity doesn’t apply equally to type parameters and type members of materialized type classes. During implicit search and subsequent type inference, whitebox type parameters are consistently erased to wildcards, whereas whitebox type members sometimes remain as is and get in the way of signature conformance checks. Unfortunately, 2.10.x can’t make use of type parameter whiteboxity, because it requires fundep materializers that were only merged into 2.11: https://github.com/scala/scala/pull/2499, and therefore Si-8104 seems to be a hard blocker for 2.10.x at the moment. Stay tuned for updates. --- test/files/neg/t8104a.check | 4 ++++ test/files/neg/t8104a/Macros_1.scala | 23 +++++++++++++++++++++++ test/files/neg/t8104a/Test_2.scala | 20 ++++++++++++++++++++ test/files/neg/t8104b.check | 4 ++++ test/files/neg/t8104b/Macros_1.scala | 23 +++++++++++++++++++++++ test/files/neg/t8104b/Test_2.scala | 24 ++++++++++++++++++++++++ 6 files changed, 98 insertions(+) create mode 100644 test/files/neg/t8104a.check create mode 100644 test/files/neg/t8104a/Macros_1.scala create mode 100644 test/files/neg/t8104a/Test_2.scala create mode 100644 test/files/neg/t8104b.check create mode 100644 test/files/neg/t8104b/Macros_1.scala create mode 100644 test/files/neg/t8104b/Test_2.scala diff --git a/test/files/neg/t8104a.check b/test/files/neg/t8104a.check new file mode 100644 index 0000000000..ef92c2e1ef --- /dev/null +++ b/test/files/neg/t8104a.check @@ -0,0 +1,4 @@ +Test_2.scala:19: error: could not find implicit value for parameter e: Generic.Aux[Test.C,(Int, Int)] + implicitly[Generic.Aux[C, (Int, Int)]] + ^ +one error found diff --git a/test/files/neg/t8104a/Macros_1.scala b/test/files/neg/t8104a/Macros_1.scala new file mode 100644 index 0000000000..688d0694c0 --- /dev/null +++ b/test/files/neg/t8104a/Macros_1.scala @@ -0,0 +1,23 @@ +import scala.reflect.macros.Context + +object Macros { + def impl[T](c: Context)(implicit T: c.WeakTypeTag[T]) = { + import c.universe._ + import Flag._ + import definitions._ + val fields = T.tpe.declarations.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x } + val Repr = appliedType(TupleClass(fields.length).asType.toType, fields.map(_.typeSignature)) + c.Expr(Block( + List(ClassDef( + Modifiers(FINAL), + newTypeName("$anon"), + List(), + Template( + List(AppliedTypeTree(Ident(newTypeName("Generic")), List(TypeTree(T.tpe)))), + emptyValDef, + List( + DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), + TypeDef(Modifiers(), newTypeName("Repr"), List(), TypeTree(Repr)))))), + Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()))) + } +} \ No newline at end of file diff --git a/test/files/neg/t8104a/Test_2.scala b/test/files/neg/t8104a/Test_2.scala new file mode 100644 index 0000000000..f601fc3570 --- /dev/null +++ b/test/files/neg/t8104a/Test_2.scala @@ -0,0 +1,20 @@ +trait Generic[T] { type Repr } +object Generic { + type Aux[T, Repr0] = Generic[T] { type Repr = Repr0 } + import scala.language.experimental.macros + implicit def materializeGeneric[T]: Generic[T] = macro Macros.impl[T] +} + +object Test extends App { + case class C(x: Int, y: Int) + + def reprify[T, Repr](x: T)(implicit generic: Generic.Aux[T, Repr]) = ??? + reprify(C(40, 2)) + + // this is a compilation error at the moment as explained in SI-8104 + // because matchesPt in implicit search says that depoly() isn't a subtype of Generic.Aux[C, (Int, Int)] + // which is rightfully so, because depoly only replaces type parameters, not type members with wildcard types + // however in the future we might want to relax the matchesPt check, so this might start compiling + // therefore, if you've broken this test, then you should be happy, because most likely you've just enabled an interesting use case! + implicitly[Generic.Aux[C, (Int, Int)]] +} diff --git a/test/files/neg/t8104b.check b/test/files/neg/t8104b.check new file mode 100644 index 0000000000..3214a132c2 --- /dev/null +++ b/test/files/neg/t8104b.check @@ -0,0 +1,4 @@ +Test_2.scala:16: error: could not find implicit value for parameter generic: Generic.Aux[Test.C,Repr] + reprify(C(40, 2)) + ^ +one error found diff --git a/test/files/neg/t8104b/Macros_1.scala b/test/files/neg/t8104b/Macros_1.scala new file mode 100644 index 0000000000..688d0694c0 --- /dev/null +++ b/test/files/neg/t8104b/Macros_1.scala @@ -0,0 +1,23 @@ +import scala.reflect.macros.Context + +object Macros { + def impl[T](c: Context)(implicit T: c.WeakTypeTag[T]) = { + import c.universe._ + import Flag._ + import definitions._ + val fields = T.tpe.declarations.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x } + val Repr = appliedType(TupleClass(fields.length).asType.toType, fields.map(_.typeSignature)) + c.Expr(Block( + List(ClassDef( + Modifiers(FINAL), + newTypeName("$anon"), + List(), + Template( + List(AppliedTypeTree(Ident(newTypeName("Generic")), List(TypeTree(T.tpe)))), + emptyValDef, + List( + DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), + TypeDef(Modifiers(), newTypeName("Repr"), List(), TypeTree(Repr)))))), + Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()))) + } +} \ No newline at end of file diff --git a/test/files/neg/t8104b/Test_2.scala b/test/files/neg/t8104b/Test_2.scala new file mode 100644 index 0000000000..a0d35942ba --- /dev/null +++ b/test/files/neg/t8104b/Test_2.scala @@ -0,0 +1,24 @@ +trait Generic[T] { type Repr } +object Generic { + type Aux[T, Repr0] = Generic[T] { type Repr = Repr0 } + import scala.language.experimental.macros + implicit def materializeGeneric[T, Repr]: Generic.Aux[T, Repr] = macro Macros.impl[T] +} + +object Test extends App { + case class C(x: Int, y: Int) + + // this doesn't work because of SI-7470 + // well, in fact SI-7470 has been fixed: https://github.com/scala/scala/pull/2499 + // it's just that the fix hasn't been backported to 2.10.x + // if you've made this compile, consider taking a look at the aforementioned pull request + def reprify[T, Repr](x: T)(implicit generic: Generic.Aux[T, Repr]) = ??? + reprify(C(40, 2)) + + // this is a compilation error at the moment as explained in SI-8104 + // because matchesPt in implicit search says that depoly() isn't a subtype of Generic.Aux[C, (Int, Int)] + // which is rightfully so, because depoly only replaces type parameters, not type members with wildcard types + // however in the future we might want to relax the matchesPt check, so this might start compiling + // therefore, if you've broken this test, then you should be happy, because most likely you've just enabled an interesting use case! + implicitly[Generic.Aux[C, (Int, Int)]] +} -- cgit v1.2.3 From 255c51b3dd342adc1cd94b4fc7668964f81480b2 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 6 Jan 2014 11:40:59 +0100 Subject: SI-6563 Test case for already-fixed crasher Progressed to working in SI-7636 / c4bf1d5. --- test/files/neg/t6563.check | 4 ++++ test/files/neg/t6563.scala | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/files/neg/t6563.check create mode 100644 test/files/neg/t6563.scala diff --git a/test/files/neg/t6563.check b/test/files/neg/t6563.check new file mode 100644 index 0000000000..75dca1507d --- /dev/null +++ b/test/files/neg/t6563.check @@ -0,0 +1,4 @@ +t6563.scala:4: error: not found: value e + e("f") + ^ +one error found diff --git a/test/files/neg/t6563.scala b/test/files/neg/t6563.scala new file mode 100644 index 0000000000..b0077b6f94 --- /dev/null +++ b/test/files/neg/t6563.scala @@ -0,0 +1,8 @@ +class A{ + def b(c: => Unit){} + b{ + e("f") + new G()(){} + } +} +class G(h:String="i")() -- cgit v1.2.3 From 2c770ae31a71d5beece4753ce4d43265036ee477 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 5 Jan 2014 22:41:51 +0100 Subject: SI-8111 Repair symbol owners after abandoned named-/default-args Names/Defaults eagerly transforms an application with temporaries to maintain evaluation order, and dutifully changes owners of symbols along the way. However, if this approach doesn't work out, we throw away this and try a auto-tupling. However, we an still witness symbols owned by the temporaries. This commit records which symbols are owned by the context.owner before `transformNamedApplication`, and rolls back the changes before `tryTupleApply`. Perhaps a better approach would be to separate the names/defaults applicability checks from the evaluation-order-preserving transform, and only call the latter after we have decided to go that way. --- .../scala/tools/nsc/typechecker/Typers.scala | 18 ++++++++++++++++ test/files/pos/t8111.scala | 24 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 test/files/pos/t8111.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 40313bdb5d..5092fdd68f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3269,6 +3269,23 @@ trait Typers extends Modes with Adaptations with Tags { // calls to the default getters. Example: // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a)) checkNotMacro() + + // SI-8111 transformNamedApplication eagerly shuffles around the application to preserve + // evaluation order. During this process, it calls `changeOwner` on symbols that + // are transplanted underneath synthetic temporary vals. + // + // Here, we keep track of the symbols owned by `context.owner` to enable us to + // rollback. Note that duplicating trees would not be enough to fix this problem, + // we would also need to clone local symbols in the duplicated tree to truly isolate + // things. + def ownerOf(sym: Symbol) = if (sym == null || sym == NoSymbol) NoSymbol else sym.owner + val symsOwnedByContextOwner = tree.collect { + case t @ (_: DefTree | _: Function) if ownerOf(t.symbol) == context.owner => t.symbol + } + def rollbackNamesDefaultsOwnerChanges() { + symsOwnedByContextOwner foreach (_.owner = context.owner) + } + val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x) if (fun1.isErroneous) duplErrTree else { @@ -3297,6 +3314,7 @@ trait Typers extends Modes with Adaptations with Tags { if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { + rollbackNamesDefaultsOwnerChanges() tryTupleApply getOrElse duplErrorTree(NotEnoughArgsError(tree, fun, missing)) } } diff --git a/test/files/pos/t8111.scala b/test/files/pos/t8111.scala new file mode 100644 index 0000000000..0d63a16ba4 --- /dev/null +++ b/test/files/pos/t8111.scala @@ -0,0 +1,24 @@ +trait T { + + def crashy(ma: Any) { + // okay + val f1 = (u: Unit) => ma + foo(f1)() + foo((u: Unit) => ma) + foo(0, (u: Any) => ma) apply () + + // crash due to side effects on the onwer of the symbol in the + // qualifier or arguments of the application during an abandoned + // names/defaults transform. The code type checkes because of + // autp-tupling which promotes and empty parmater list to `(): Unit` + foo((u: Any) => ma)() + + {{(u: Any) => ma}; this}.foo(0)() + + foo({def foo = ma; 0})() + + {def foo = ma; this}.foo(0)() + } + + def foo(f: Any): Any => Any +} -- cgit v1.2.3 From c91d373a78f0c503ddc635bce1974c1b58008219 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 8 Jan 2014 12:15:04 +0100 Subject: SI-8111 Expand the comment with a more detailed TODO As everone knows, undo/reset/retype/rollback are bandaids; we should try to treat the disease more directly. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 5092fdd68f..e09a509839 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3275,9 +3275,14 @@ trait Typers extends Modes with Adaptations with Tags { // are transplanted underneath synthetic temporary vals. // // Here, we keep track of the symbols owned by `context.owner` to enable us to - // rollback. Note that duplicating trees would not be enough to fix this problem, - // we would also need to clone local symbols in the duplicated tree to truly isolate - // things. + // rollback, so that we don't end up with "orphaned" symbols. + // + // TODO: Find a better way! + // + // Note that duplicating trees would not be enough to fix this problem, we would also need to + // clone local symbols in the duplicated tree to truly isolate things (in the spirit of BodyDuplicator), + // or, better yet, disentangle the logic in `transformNamedApplication` so that we could + // determine whether names/defaults is viable *before* transforming trees. def ownerOf(sym: Symbol) = if (sym == null || sym == NoSymbol) NoSymbol else sym.owner val symsOwnedByContextOwner = tree.collect { case t @ (_: DefTree | _: Function) if ownerOf(t.symbol) == context.owner => t.symbol -- cgit v1.2.3 From bd4adf5c97bdebca47923a919c07f4363838be83 Mon Sep 17 00:00:00 2001 From: James Ward Date: Thu, 24 Oct 2013 09:32:22 -0600 Subject: More clear implicitNotFound error for ExecutionContext --- src/library/scala/concurrent/ExecutionContext.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 8928724b34..b4af161c3c 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -16,7 +16,7 @@ import scala.util.Try /** * An `ExecutionContext` is an abstraction over an entity that can execute program logic. */ -@implicitNotFound("Cannot find an implicit ExecutionContext, either require one yourself or import ExecutionContext.Implicits.global") +@implicitNotFound("Cannot find an implicit ExecutionContext, either import scala.concurrent.ExecutionContext.Implicits.global or use a custom one") trait ExecutionContext { /** Runs a block of code on this execution context. -- cgit v1.2.3 From 5876e8c62130b4089b146d4fea5bcd926e47bf6f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 9 Jan 2014 13:58:09 +0100 Subject: [nomaster] SI-8114 Binary compat. workaround for erasure bug SI-7120 We can't backport SI-7120 to 2.10.x as it changes erased signatures, which can lead to interop problems between 2.10.3 and 2.10.4. But, we can detect one of the nasty symptoms -- a bridge method with the same signature as its target -- and treat that. This commit detects duplicate bridges in the ASM (only) backend and removes them. --- .../scala/tools/nsc/backend/jvm/GenASM.scala | 27 +++++++++++++++++++++- test/files/run/t8114.scala | 15 ++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t8114.scala diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 19cdcd2590..3712745590 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -1397,13 +1397,38 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } clasz.fields foreach genField - clasz.methods foreach { im => genMethod(im, c.symbol.isInterface) } + clasz.methods foreach { im => + if (im.symbol.isBridge && isRedundantBridge(im, clasz)) + // We can't backport the erasure fix of SI-7120 to 2.10.x, but we can detect and delete + // bridge methods with identical signatures to their targets. + // + // NOTE: this backstop only implemented here in the ASM backend, and is not implemented in the FJBG backend. + debugwarn(s"Discarding redundant bridge method: ${im.symbol.debugLocationString}. See SI-8114.") + else + genMethod(im, c.symbol.isInterface) + } addInnerClasses(clasz.symbol, jclass) jclass.visitEnd() writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol) } + private def isRedundantBridge(bridge: IMethod, owner: IClass): Boolean = { + def lastCalledMethod: Option[Symbol] = bridge.code.instructions.reverseIterator.collectFirst { + case CALL_METHOD(meth, _) => meth + } + def hasSameSignatureAsBridge(targetMethod: Symbol): Boolean = { + val targetIMethod = clasz.methods find (m => m.symbol == targetMethod) + // Important to compare the IMethod#paramss, rather then the erased MethodTypes, as + // due to the bug SI-7120, these are out of sync. For example, in the `applyOrElse` + // method in run/t8114.scala, the method symbol info has a parameter of type `Long`, + // but the IMethod parameter has type `Object`. The latter comes from the info of the + // symbol representing the parameter ValDef in the tree, which is incorrectly erased. + targetIMethod exists (m => bridge.matchesSignature(m)) + } + lastCalledMethod exists hasSameSignatureAsBridge + } + /** * @param owner internal name of the enclosing class of the class. * diff --git a/test/files/run/t8114.scala b/test/files/run/t8114.scala new file mode 100644 index 0000000000..ecbca37d2a --- /dev/null +++ b/test/files/run/t8114.scala @@ -0,0 +1,15 @@ +class AbstractTable[T] { type TableElementType } +class Table[T] extends AbstractTable[T] { type TableElementType = T } + +class Query[E, U] +class TableQuery[E <: AbstractTable[_]] extends Query[E, E#TableElementType] + +object Test extends App { + object MyTable extends TableQuery[Table[Long]] + + def list[R](q: Query[_, R]): List[R] = Nil + list/*[Long]*/(MyTable) collect { case x => x } + + // Generates a redundant bridge method (double definition error) + // in 2.10.x due to (at least) the bug in erasure fixed in SI-7120 +} -- cgit v1.2.3 From 9df2dcc58439cf75420da68d4e6d9bb5504aabb4 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 14 Jan 2014 16:40:25 +0100 Subject: [nomaster] SI-8152 Backport variance validator performance fix % time qbin/scalac test/files/pos/t8146-performance.scala real 0m2.015s user 0m2.892s sys 0m0.215s % time scalac-hash v2.10.3 test/files/pos/t8146-performance.scala real 1m13.652s user 1m14.245s sys 0m0.508s Cherry-picks one hunk from 882f8e64. --- src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 8 +++++--- test/files/pos/t8152-performance.scala | 13 +++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 test/files/pos/t8152-performance.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 081f7a8696..fea234dd14 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -909,11 +909,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // case DeBruijnIndex(_, _) => case SingleType(pre, sym) => validateVariance(pre, variance) + case TypeRef(_, sym, _) if sym.isAliasType => + // okay to ignore pre/args here. In 2.10.3 we used to check them in addition to checking + // the normalized type, which led to exponential time type checking, see pos/t8152-performance.scala + validateVariance(tp.normalize, variance) case TypeRef(pre, sym, args) => // println("validate "+sym+" at "+relativeVariance(sym)) - if (sym.isAliasType/* && relativeVariance(sym) == AnyVariance*/) - validateVariance(tp.normalize, variance) - else if (sym.variance != NoVariance) { + if (sym.variance != NoVariance) { val v = relativeVariance(sym) if (v != AnyVariance && sym.variance != v * variance) { //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG diff --git a/test/files/pos/t8152-performance.scala b/test/files/pos/t8152-performance.scala new file mode 100644 index 0000000000..b6d2ecd823 --- /dev/null +++ b/test/files/pos/t8152-performance.scala @@ -0,0 +1,13 @@ +class HListBench { + + class A[H, T] + + type B[H, T] = A[H, T] + + // was okay + type T1 = A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, A[Int, Nothing]]]]]]]]]]]]]]]]]]]]]]]]]]]] + + // Took over a minute to validate variance in 2.10.3! + type T2 = B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, B[Int, Nothing]]]]]]]]]]]]]]]]]]]]]]]]]]]] + +} \ No newline at end of file -- cgit v1.2.3