From fc7cb9a91918cfdf7afe0600a87290ff71f88f12 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 13 Jul 2015 10:13:23 +1000 Subject: SI-9392 Avoid crash in GenBCode for incoherent trees A macro in shapeless was generating a tree of the form: ``` { class C#2 new C#2 }.setType(C#1) ``` This happened due to an error in the macro; it used untypecheck to try to fix the owner-chain consistency problem, but kept a reference to the previous version of the block-local class symbol `C` and used this in the resulting tree. This commit detects the particular situation we encountered, and avoids the crash by not creating the `NestedInfo` for the `BType` corresponding to `C#1`. The code comment discusses why I think this is safe, and suggests a refactoring that would mean we only ever try to construct `NestedInfo` when we are going to need them. --- .../scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala | 8 ++++++++ test/files/pos/t9392/client_2.scala | 4 ++++ test/files/pos/t9392/macro_1.scala | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 test/files/pos/t9392/client_2.scala create mode 100644 test/files/pos/t9392/macro_1.scala diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 5f8f0e167c..85f07576ab 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -351,6 +351,14 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { val isTopLevel = innerClassSym.rawowner.isPackageClass // impl classes are considered top-level, see comment in BTypes if (isTopLevel || considerAsTopLevelImplementationArtifact(innerClassSym)) None + else if (innerClassSym.rawowner.isTerm) + // SI-9392 An errant macro might leave a reference to a local class symbol that no longer exists in the tree, + // this avoids an assertion error in that case. AFAICT, we don't actually need the `NestedInfo` for all BTypes, + // only for ones that describe classes defined in the trees that reach the backend, so this is safe enough. + // + // TODO Can we avoid creating `NestedInfo` for each type that is referred to, and instead only create if for + // symbols of ClassDefs? + None else { // See comment in BTypes, when is a class marked static in the InnerClass table. val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner) diff --git a/test/files/pos/t9392/client_2.scala b/test/files/pos/t9392/client_2.scala new file mode 100644 index 0000000000..6b706fea12 --- /dev/null +++ b/test/files/pos/t9392/client_2.scala @@ -0,0 +1,4 @@ +class Client { + Macro() +} + diff --git a/test/files/pos/t9392/macro_1.scala b/test/files/pos/t9392/macro_1.scala new file mode 100644 index 0000000000..3f67ac17b2 --- /dev/null +++ b/test/files/pos/t9392/macro_1.scala @@ -0,0 +1,16 @@ +import language.experimental.macros + + +object Macro { + + import reflect.macros.blackbox.Context + def impl(c: Context)(): c.Tree = { + import c.universe._ + val tree = q"""class C; new C""" + val tree1 = c.typecheck(tree) + val tpe = tree1.tpe + val tree2 = c.typecheck(c.untypecheck(tree1)) + q"""$tree2.asInstanceOf[$tpe]""" + } + def apply(): Any = macro impl +} -- cgit v1.2.3