diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2015-03-11 11:34:08 -0700 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2015-03-11 12:53:36 -0700 |
commit | 027e97981d9b6a3783e9ab247cc898017b3de821 (patch) | |
tree | 209a0d646f54c14b119924ba55773c382f50a701 | |
parent | 4e982451decdc3821febfe975e1b8e406a3741e8 (diff) | |
download | scala-027e97981d9b6a3783e9ab247cc898017b3de821.tar.gz scala-027e97981d9b6a3783e9ab247cc898017b3de821.tar.bz2 scala-027e97981d9b6a3783e9ab247cc898017b3de821.zip |
Workaround for SI-9111
The inliner forces some method symbols to complete that would not be
completed otherwise. This triggers SI-9111, in which the completer of
a valid Java method definition reports an error in mixed compilation.
The workaround disables error reporting while completing lazy method
and class symbols in the backend.
6 files changed, 84 insertions, 16 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index e617c86b23..81d8adb7de 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -12,7 +12,7 @@ import asm.Opcodes import scala.tools.asm.tree.{InnerClassNode, ClassNode} import scala.tools.nsc.backend.jvm.BTypes.{MethodInlineInfo, InlineInfo} import scala.tools.nsc.backend.jvm.opt.{CallGraph, ByteCodeRepository, Inliner} -import OptimizerReporting._ +import opt.OptimizerReporting._ import scala.collection.convert.decorateAsScala._ /** @@ -736,8 +736,10 @@ abstract class BTypes { /** * A ClassBType represents a class or interface type. The necessary information to build a * ClassBType is extracted from compiler symbols and types, see BTypesFromSymbols. + * + * Currently non-final due to SI-9111 */ - final case class ClassBType(internalName: InternalName) extends RefBType { + /*final*/ case class ClassBType(internalName: InternalName) extends RefBType { /** * Write-once variable allows initializing a cyclic graph of infos. This is required for * nested classes. Example: for the definition `class A { class B }` we have diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index d94bd77851..9fdb92b47c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -146,14 +146,49 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { else { val internalName = classSym.javaBinaryName.toString classBTypeFromInternalName.getOrElse(internalName, { - // The new ClassBType is added to the map in its constructor, before we set its info. This - // allows initializing cyclic dependencies, see the comment on variable ClassBType._info. - setClassInfo(classSym, ClassBType(internalName)) + if (completeSilentlyAndCheckErroneous(classSym)) { + new ErroneousClassBType(internalName) + } else { + // The new ClassBType is added to the map in its constructor, before we set its info. This + // allows initializing cyclic dependencies, see the comment on variable ClassBType._info. + setClassInfo(classSym, ClassBType(internalName)) + } }) } } /** + * Part of the workaround for SI-9111. Makes sure that the compiler only fails if the ClassInfo + * of the symbol that could not be completed is actually required. + */ + private class ErroneousClassBType(internalName: InternalName) extends ClassBType(internalName) { + def msg = s"The class info for $internalName could not be completed due to SI-9111." + override def info: ClassInfo = opt.OptimizerReporting.assertionError(msg) + override def info_=(i: ClassInfo): Unit = opt.OptimizerReporting.assertionError(msg) + } + + /** + * This is a hack to work around SI-9111. The completer of `methodSym` may report type errors. We + * cannot change the typer context of the completer at this point and make it silent: the context + * captured when creating the completer in the namer. However, we can temporarily replace + * global.reporter (it's a var) to store errors. + */ + def completeSilentlyAndCheckErroneous(sym: Symbol): Boolean = { + if (sym.rawInfo.isComplete) false + else { + val originalReporter = global.reporter + val storeReporter = new reporters.StoreReporter() + try { + global.reporter = storeReporter + sym.info + } finally { + global.reporter = originalReporter + } + storeReporter.infos.exists(_.severity == storeReporter.ERROR) + } + } + + /** * Builds a [[MethodBType]] for a method symbol. */ final def methodBTypeFromSymbol(methodSymbol: Symbol): MethodBType = { @@ -428,18 +463,24 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { else { // Primitve methods cannot be inlined, so there's no point in building an InlineInfo. Also, some // primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]]. - classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).map({ + classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).flatMap({ case methodSym => - val methodBType = methodBTypeFromSymbol(methodSym) - val name = methodSym.javaSimpleName.toString // same as in genDefDef - val signature = name + methodBType.descriptor - val info = MethodInlineInfo( - effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden, - traitMethodWithStaticImplementation = false, // temporary, fixed in future commit - annotatedInline = methodSym.hasAnnotation(ScalaInlineClass), - annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass) - ) - (signature, info) + if (completeSilentlyAndCheckErroneous(methodSym)) { + // Happens due to SI-9111. Just don't provide any InlineInfo for that method, we don't + // need fail the compiler. + None + } else { + val methodBType = methodBTypeFromSymbol(methodSym) + val name = methodSym.javaSimpleName.toString // same as in genDefDef + val signature = name + methodBType.descriptor + val info = MethodInlineInfo( + effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden, + traitMethodWithStaticImplementation = false, // temporary, fixed in future commit + annotatedInline = methodSym.hasAnnotation(ScalaInlineClass), + annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass) + ) + Some((signature, info)) + } }).toMap } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala index 53c00c7724..5b47bc88c2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala @@ -5,6 +5,7 @@ package scala.tools.nsc package backend.jvm +package opt import scala.tools.asm import asm.tree._ diff --git a/test/files/pos/t9111-inliner-workaround.flags b/test/files/pos/t9111-inliner-workaround.flags new file mode 100644 index 0000000000..63b5558cfd --- /dev/null +++ b/test/files/pos/t9111-inliner-workaround.flags @@ -0,0 +1 @@ +-Ybackend:GenBCode -Yopt:l:classpath
\ No newline at end of file diff --git a/test/files/pos/t9111-inliner-workaround/A_1.java b/test/files/pos/t9111-inliner-workaround/A_1.java new file mode 100644 index 0000000000..bc60b68ea6 --- /dev/null +++ b/test/files/pos/t9111-inliner-workaround/A_1.java @@ -0,0 +1,13 @@ +public class A_1 { + public static class T { } + + public static class Inner { + public static class T { } + + public void foo(T t) { } + + public T t = null; + + public class Deeper extends T { } + } +} diff --git a/test/files/pos/t9111-inliner-workaround/Test_1.scala b/test/files/pos/t9111-inliner-workaround/Test_1.scala new file mode 100644 index 0000000000..1a00fff833 --- /dev/null +++ b/test/files/pos/t9111-inliner-workaround/Test_1.scala @@ -0,0 +1,10 @@ +object Test extends App { + println(new A_1.Inner()) + + // Accessing foo or Deeper triggers the error of SI-9111. + // However, when not referring to those definitions, compilation should + // succeed, also if the inliner is enabled. + + // println(i.foo(null)) + // new i.Deeper() +} |