Skip to content

Commit

Permalink
SI-7242 Fix crash when inner object mixes in its companion
Browse files Browse the repository at this point in the history
Given:

    class C {
      trait T { C.this }            // C$T$$$outer$ : C
      object T extends T { C.this } // C$T$$$outer$ : C.this.type
    }

object T ended up with a definitions for both of the accessors.
These cannot co-exist, as they have the same erased type. A crash
ensued almost immediately in explitouter.

Fortunately, the solution is straightforward: we can just omit
the mixin outer accessor altogether, the objects own outer accessor
is compatible with it.

    scala> :javap C.T
    Compiled from "<console>"
    public interface C$T{
        public abstract C C$T$$$outer();
    }

    scala> :javap C.T$
    Compiled from "<console>"
    public class C$T$ extends java.lang.Object implements C$T{
        public C C$T$$$outer();
        public C$T$(C);
    }

I also added an assertion to give a better error message in
case we find ourselves here again.

Also includes tests from SI-3994, which I'll mark as a duplicate.
  • Loading branch information
retronym committed Mar 13, 2013
1 parent 52adf13 commit 2b5fde7
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 5 deletions.
38 changes: 33 additions & 5 deletions src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
Expand Up @@ -112,6 +112,29 @@ abstract class ExplicitOuter extends InfoTransform
sym setInfo clazz.outerClass.thisType
}

/**
* Will the outer accessor of the `clazz` subsume the outer accessor of
* `mixin`?
*
* This arises when an inner object mixes in its companion trait.
*
* {{{
* class C {
* trait T { C.this } // C$T$$$outer$ : C
* object T extends T { C.this } // C$T$$$outer$ : C.this.type
* }
* }}}
*
* See SI-7242.
}}
*/
private def skipMixinOuterAccessor(clazz: Symbol, mixin: Symbol) = {
// Reliant on the current scheme for name expansion, the expanded name
// of the outer accessors in a trait and its companion object are the same.
// If the assumption is one day falsified, run/t7424.scala will let us know.
clazz.fullName == mixin.fullName
}

/** <p>
* The type transformation method:
* </p>
Expand Down Expand Up @@ -177,10 +200,14 @@ abstract class ExplicitOuter extends InfoTransform
for (mc <- clazz.mixinClasses) {
val mixinOuterAcc: Symbol = afterExplicitOuter(outerAccessor(mc))
if (mixinOuterAcc != NoSymbol) {
if (decls1 eq decls) decls1 = decls.cloneScope
val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED)
newAcc setInfo (clazz.thisType memberType mixinOuterAcc)
decls1 enter newAcc
if (skipMixinOuterAccessor(clazz, mc))
debuglog(s"Reusing outer accessor symbol of $clazz for the mixin outer accessor of $mc")
else {
if (decls1 eq decls) decls1 = decls.cloneScope
val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED)
newAcc setInfo (clazz.thisType memberType mixinOuterAcc)
decls1 enter newAcc
}
}
}
}
Expand Down Expand Up @@ -390,6 +417,7 @@ abstract class ExplicitOuter extends InfoTransform
val outerAcc = outerAccessor(mixinClass) overridingSymbol currentClass
def mixinPrefix = (currentClass.thisType baseType mixinClass).prefix
assert(outerAcc != NoSymbol, "No outer accessor for inner mixin " + mixinClass + " in " + currentClass)
assert(outerAcc.alternatives.size == 1, s"Multiple outer accessors match inner mixin $mixinClass in $currentClass : ${outerAcc.alternatives.map(_.defString)}")
// I added the mixinPrefix.typeArgs.nonEmpty condition to address the
// crash in SI-4970. I feel quite sure this can be improved.
val path = (
Expand Down Expand Up @@ -492,7 +520,7 @@ abstract class ExplicitOuter extends InfoTransform
}
if (!currentClass.isTrait)
for (mc <- currentClass.mixinClasses)
if (outerAccessor(mc) != NoSymbol)
if (outerAccessor(mc) != NoSymbol && !skipMixinOuterAccessor(currentClass, mc))
newDefs += mixinOuterAccessorDef(mc)
}
}
Expand Down
20 changes: 20 additions & 0 deletions test/files/run/t3994.scala
@@ -0,0 +1,20 @@
trait T {
trait Default { def foo = this }
object Default extends Default
}

class Crash { // if you change this to a `trait` it keeps failing, though if it is an `object` it compiles just fine!
class Element

/* declare this as a class, and the crash goes away */
trait ElementOrdering extends Ordering[Element] {
def compare(a: Element, b: Element): Int = 0
}

implicit object ElementOrdering extends ElementOrdering
}

object Test extends App {
(new T {}).Default
(new Crash).ElementOrdering
}
71 changes: 71 additions & 0 deletions test/files/run/t7242.scala
@@ -0,0 +1,71 @@
class CrashTest {
def foo = ()
trait CrashTestTable {
def cols = foo
}
// This was leading to a class between the mixed in
// outer accessor and the outer accessor of this object.
object CrashTestTable extends CrashTestTable {
foo
cols
}
}

class CrashTest1 {
def foo = ()
class CrashTestTable {
def cols = foo
}
object CrashTestTable extends CrashTestTable {
foo
cols
}
}

class CrashTest2 {
def foo = ()
trait CrashTestTable {
def cols = foo
}
object Obj extends CrashTestTable {
foo
cols
}
}

class CrashTest3 {
def foo = ()

def meth() {
trait CrashTestTable {
def cols = foo
}
object Obj extends CrashTestTable {
foo
cols
}
Obj
}
}

object Test extends App {
{
val c = new CrashTest
c.CrashTestTable
}

{
val c = new CrashTest1
c.CrashTestTable
}

{
val c = new CrashTest2
c.Obj
}

{
val c = new CrashTest3
c.meth()
}
}

0 comments on commit 2b5fde7

Please sign in to comment.