correct outer check for match on final inner class #4440
Comments
Imported From: https://issues.scala-lang.org/browse/SI-4440?orig=1 |
@magarciaEPFL said: package <empty> {
final class Outer extends java.lang.Object with ScalaObject {
@volatile protected var bitmap$$0: Int = 0;
<synthetic> lazy private[this] var Inner1$$module: object Outer$$Inner1 = _;
<synthetic> <stable> <accessor> lazy def Inner1(): object Outer$$Inner1 = {
if (Outer.this.bitmap$$0.&(1).==(0))
{
Outer.this.synchronized({
if (Outer.this.bitmap$$0.&(1).==(0))
{
Outer.this.Inner1$$module = new object Outer$$Inner1(Outer.this);
Outer.this.bitmap$$0 = Outer.this.bitmap$$0.|(1);
()
};
scala.runtime.BoxedUnit.UNIT
});
()
};
Outer.this.Inner1$$module
};
private[this] val inner: Outer$$Inner = _;
<stable> <accessor> def inner(): Outer$$Inner = Outer.this.inner;
def bar(): Int = {
<synthetic> val temp5: Outer$$Inner = Outer.this.inner();
if (temp5.$$isInstanceOf[Outer$$Inner1]().&&(temp5.$$asInstanceOf[Outer$$Inner1]().Outer$$Inner1$$$$$$outer().eq(Outer.this)))
{
temp5.$$asInstanceOf[Outer$$Inner1]().foo()
}
else
throw new MatchError(temp5)
};
def this(): Outer = {
Outer.super.this();
Outer.this.inner = new Outer$$Inner1(Outer.this, 0);
()
}
};
final object Crash extends java.lang.Object with ScalaObject {
def main(args: Array[java.lang.String]): Unit = scala.this.Predef.println(scala.Int.box(new Outer().bar()));
def this(): object Crash = {
Crash.super.this();
()
}
};
sealed abstract trait Outer$$Inner extends java.lang.Object;
final case class Outer$$Inner1 extends java.lang.Object with Outer$$Inner with ScalaObject with Product with Serializable {
def productIterator(): Iterator = scala.Product$$class.productIterator(Outer$$Inner1.this);
@deprecated("use productIterator instead") def productElements(): Iterator = scala.Product$$class.productElements(Outer$$Inner1.this);
<caseaccessor> <paramaccessor> private[this] val foo: Int = _;
<stable> <caseaccessor> <accessor> <paramaccessor> def foo(): Int = Outer$$Inner1.this.foo;
<synthetic> def copy(foo: Int = foo): Outer$$Inner1 = new Outer$$Inner1(Outer$$Inner1.this.$$outer, foo);
<synthetic> def copy$$default$$1(): Int = Outer$$Inner1.this.foo();
override def hashCode(): Int = ScalaRunTime.this._hashCode(Outer$$Inner1.this);
override def toString(): java.lang.String = ScalaRunTime.this._toString(Outer$$Inner1.this);
override def equals(x$$1: java.lang.Object): Boolean = Outer$$Inner1.this.eq(x$$1).||({
{
<synthetic> val temp1: java.lang.Object = x$$1;
if (temp1.$$isInstanceOf[Outer$$Inner1]())
{
<synthetic> val temp2: Outer$$Inner1 = temp1.$$asInstanceOf[Outer$$Inner1]();
<synthetic> val temp3: Int = temp2.foo();
val foo$$1: Int = temp3;
if (Outer$$Inner1.this.gd1$$1(foo$$1))
{
x$$1.$$asInstanceOf[Outer$$Inner1]().canEqual(Outer$$Inner1.this)
}
else
{
false
}
}
else
{
false
}
}
});
override def productPrefix(): java.lang.String = "Inner1";
override def productArity(): Int = 1;
override def productElement(x$$1: Int): java.lang.Object = {
<synthetic> val temp4: Int = x$$1;
if (temp4.==(0))
{
scala.Int.box(Outer$$Inner1.this.foo())
}
else
{
throw new java.lang.IndexOutOfBoundsException(scala.Int.box(x$$1).toString())
}
};
override def canEqual(x$$1: java.lang.Object): Boolean = x$$1.$$isInstanceOf[Outer$$Inner1]();
<synthetic> <paramaccessor> private[this] val $$outer: Outer = _;
final <synthetic> private[this] def gd1$$1(x$$1: Int): Boolean = x$$1.==(Outer$$Inner1.this.foo());
def this($$outer: Outer, foo: Int): Outer$$Inner1 = {
Outer$$Inner1.this.foo = foo;
if ($$outer.eq(null))
throw new java.lang.NullPointerException()
else
Outer$$Inner1.this.$$outer = $$outer;
Outer$$Inner1.super.this();
scala.Product$$class./*Product$$class*/$$init$$(Outer$$Inner1.this);
()
}
};
final <synthetic> object Outer$$Inner1 extends scala.runtime.AbstractFunction1 with ScalaObject with Serializable {
final override def toString(): java.lang.String = "Inner1";
case <synthetic> def unapply(x$$0: Outer$$Inner1): Option = if (x$$0.==(null))
scala.this.None
else
new Some(scala.Int.box(x$$0.foo()));
case <synthetic> def apply(foo: Int): Outer$$Inner1 = new Outer$$Inner1(Outer$$Inner1.this.$$outer, foo);
<synthetic> <paramaccessor> private[this] val $$outer: Outer = _;
case <synthetic> <bridge> def apply(v1: java.lang.Object): java.lang.Object = Outer$$Inner1.this.apply(scala.Int.unbox(v1));
def this($$outer: Outer): object Outer$$Inner1 = {
if ($$outer.eq(null))
throw new java.lang.NullPointerException()
else
Outer$$Inner1.this.$$outer = $$outer;
Outer$$Inner1.super.this();
()
}
}
} |
@lrytz said: |
@lexn82 said: object Test {
def main(args: Array[String]) {
(new Clazz).matcher()
}
}
final class Clazz {
private final class SubClazz {
def foo() = 1
}
private val array = new Array[AnyRef](2)
(0 until 2).foreach(i => array(i) = new SubClazz)
def matcher() {
var i = 0; while (i < array.size) { val c = array(i)
c match {
case s: SubClazz => println(s)
case _ => println("no match")
}
}
}
} |
@soc said (edited on Apr 20, 2012 5:55:53 PM UTC): trait ContextContainer {
def Contexts: Contexts
abstract class Contexts extends swing.Publisher {
trait Context extends swing.Publisher {
Contexts.this.listenTo(this)
def fireChanged() {publish(ContextChanged(this))}
}
reactions += {case c @ ContextChanged(_) => publish(c)}
}
final case class ContextChanged(c: Contexts#Context) extends swing.event.Event
}
object ContextsTest extends App with ContextContainer {
object Contexts extends Contexts {
case class NamedContext(name: String) extends Context
val C1 = NamedContext("C1")
}
Contexts.C1.fireChanged() //java.lang.NoSuchMethodError: ContextContainer$ContextChanged.ContextContainer$ContextChanged$$$outer()LContextContainer;
}
java.lang.NoSuchMethodError: ContextContainer$ContextChanged.$line37$$read$ContextContainer$ContextChanged$$$outer()LContextContainer;
at ContextContainer$Contexts$$anonfun$1.isDefinedAt(<console>:18)
at ContextContainer$Contexts$$anonfun$1.isDefinedAt(<console>:18)
at scala.swing.Reactions$Impl$$anonfun$apply$1.apply(Reactions.scala:25)
at scala.swing.Reactions$Impl$$anonfun$apply$1.apply(Reactions.scala:25)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:77)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:31)
at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:45)
at scala.swing.Reactions$Impl.apply(Reactions.scala:25)
at scala.swing.Reactions$Impl.apply(Reactions.scala:19)
at scala.Function1$class.apply$mcVL$sp(Function1.scala:39)
at scala.swing.Reactions.apply$mcVL$sp(Reactions.scala:46)
at scala.swing.Publisher$$anonfun$publish$1.apply(Publisher.scala:47)
at scala.swing.Publisher$$anonfun$publish$1.apply(Publisher.scala:47)
at scala.collection.Iterator$class.foreach(Iterator.scala:697)
at scala.swing.SingleRefCollection$$anon$4.foreach(Publisher.scala:108)
at scala.collection.IterableLike$class.foreach(IterableLike.scala:73)
at scala.swing.RefSet.foreach(Publisher.scala:165)
at scala.swing.Publisher$class.publish(Publisher.scala:47)
at ContextsTest$Contexts$NamedContext.publish(<console>:26)
at ContextContainer$Contexts$Context$class.fireChanged(<console>:16)
at ContextsTest$Contexts$NamedContext.fireChanged(<console>:26)
at ContextsTest$delayedInit$body.apply(<console>:30)
at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App$$anonfun$main$1.apply(App.scala:61)
at scala.App$$anonfun$main$1.apply(App.scala:61)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)
at scala.collection.immutable.List.foreach(List.scala:77)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:31)
at scala.App$class.main(App.scala:61)
at ContextsTest$.main(<console>:24)
at .<init>(<console>:35)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:775)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$16.apply(IMain.scala:1039)
at scala.tools.nsc.interpreter.Line.scala$tools$nsc$interpreter$Line$$runAndSetState(Line.scala:41)
at scala.tools.nsc.interpreter.Line$$anonfun$2.apply$mcV$sp(Line.scala:47)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:22)
at java.lang.Thread.run(Thread.java:679) |
@hubertp said: |
@retronym said (edited on Jun 22, 2012 6:35:35 AM UTC): class Outer {
final class Inner {
// Main$$anon$Outer$Inner1$$$outer removed in the constructors phase.
// because Inner1 is effectively final. (Remove `final` and things work.)
// See:
// $outer method elimination: https://github.com/scala/scala/blob/3631f1d/src/compiler/scala/tools/nsc/transform/Constructors.scala#L226
// outer test in pattern matcher: https://github.com/scala/scala/blob/8284486/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala#L937
// Supposed elimintion of the outer test: https://github.com/scala/scala/blob/8284486/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala#L525
}
def bar2 = (new Inner: Any) match {
case i: Inner =>
}
}
println((new Outer).bar2)
// java.lang.NoSuchMethodError: Main$$anon$1$Outer$Inner1.Main$$anon$Outer$Inner1$$$outer()LMain$$anon$1$Outer;
// <synthetic> <stable> def Main$$anon$Outer$Inner1$$$outer(): Outer = Inner1.this.$outer
|
@adriaanm said (edited on Jul 19, 2012 10:17:39 AM UTC): Why single out final classes in the first place? |
@adriaanm said: |
@adriaanm said: (#951 was rejected because a proper fix would increase memory footprint) |
@adriaanm said: In the mean time, if you don't want an outer pointer, don't nest your class. |
@retronym said (edited on Mar 2, 2013 8:11:25 AM UTC): |
@Blaisorblade said: scala/scala#2634 (comment) I might post here an updated summary in my copious free time, but until then, I hope that link helps. |
@Blaisorblade said:
Here's also a link to the email conversation, for completeness (not sure it contains anything more): |
@dcsobral said: |
Malcolm Greaves (malcolmgreaves) said (edited on Nov 24, 2015 12:13:45 AM UTC): trait BinaryTree {
trait Payload
sealed trait Node
case class Parent(left: Option[Node], right: Option[Node]) extends Node
case class Leaf(data: Payload) extends Node
} However, if I add
(Note: I get the same error message for both My current solution is to use trait BinaryTree {
trait Payload
sealed trait Node
sealed case class Parent(left: Option[Node], right: Option[Node]) extends Node
sealed case class Leaf(data: Payload) extends Node
} Any thoughts on this? I'd like to express the fact that this ADT definition cannot be changed. Including the definitions for Also, I hope that this can jump-start this stale ticket. |
@DarkDimius said: |
Here's another instance of this bug along with another workaround: scalameta/scalameta#900 (comment). |
It seems something like this bug can also be hit when you have aliases then try to pattern match on the
For example: in TypedPipe above we have a number of final case classes inside the object. We reexport com.twitter.scalding.typed.TypedPipe as com.twitter.scalding.TypedPipe with the following in the package object: val TypedPipe = com.twitter.scalding.typed.TypedPipe
type TypedPipe[+T] = com.twitter.scalding.typed.TypedPipe[T] using the un-aliased types seems to side-step the the issue. |
@johnynek This is how we work around the problem in Scalameta. Annoying but effective: https://github.com/scalameta/scalameta/blob/v3.2.0/langmeta/langmeta/shared/src/main/scala/org/langmeta/inputs/Api.scala. |
Here's the simplest reproducer I could find:
Changing the
|
Adriaan also gave some examples in scala-internals in 2013: https://groups.google.com/d/msg/scala-internals/vw8Kek4zlZ8/EMipRxRVQi0J
|
It seems there are a few issues here:
|
+1 on all your points. We should not use |
spores use macros (which I thought are kind of on the chopping block), and have not been published for 2.12: https://search.maven.org/#search%7Cga%7C1%7Cch.epfl.scala.spores Is there any chance to get a supported version of spores into scala? It would be great for GC sensitive apps (controlling what you close over) as well as serialization cases (spark, scalding, etc...) |
@johnynek FWIW for spores 2.12, see scalacenter/spores#21 (that's currently the master repo), but I'll leave your other questions to others. |
There remains only one which I do not fully understand. It is relevant to this compiler bug: scala/bug#4440 The rest has been fixed, by: - fixing build encoding - explicitly setting scalac flags - specifying missing artefact versions - rewriting exception assertions - replacing deprecated data structures
There remains only one which I do not fully understand. It is relevant to this compiler bug: scala/bug#4440 The rest has been fixed, by: - fixing build encoding - explicitly setting scalac flags - specifying missing artefact versions - rewriting exception assertions - replacing deprecated data structures
Case classes, such as final case classes nested within other classes, lose their outer pointer which makes their equality lie (scala/bug#4440): scala> class Outer { final case class Inner(n: Int) } warning: there was one unchecked warning; for details, enable `:setting -unchecked' or `:replay -unchecked' defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@627bcd7e o2: Outer = Outer@70543cae scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = true vs scala> class Outer { case class Inner(n: Int) } defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@77ba583 o2: Outer = Outer@5613247e scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = false This isn't a problem in the REPL even with its class-based wrappers as there is only ever 1 instance of (each) wrapper class.
Case classes, such as final case classes nested within other classes, lose their outer pointer which makes their equality lie (scala/bug#4440): scala> class Outer { final case class Inner(n: Int) } warning: there was one unchecked warning; for details, enable `:setting -unchecked' or `:replay -unchecked' defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@627bcd7e o2: Outer = Outer@70543cae scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = true vs scala> class Outer { case class Inner(n: Int) } defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@77ba583 o2: Outer = Outer@5613247e scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = false This isn't a problem in the REPL even with its class-based wrappers as there is only ever 1 instance of (each) wrapper class.
Case classes, such as final case classes nested within other classes, lose their outer pointer which makes their equality lie (scala/bug#4440): scala> class Outer { final case class Inner(n: Int) } warning: there was one unchecked warning; for details, enable `:setting -unchecked' or `:replay -unchecked' defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@627bcd7e o2: Outer = Outer@70543cae scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = true vs scala> class Outer { case class Inner(n: Int) } defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@77ba583 o2: Outer = Outer@5613247e scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = false This isn't a problem in the REPL even with its class-based wrappers as there is only ever 1 instance of (each) wrapper class.
Case classes, such as final case classes nested within other classes, lose their outer pointer which makes their equality lie (scala/bug#4440): scala> class Outer { final case class Inner(n: Int) } warning: there was one unchecked warning; for details, enable `:setting -unchecked' or `:replay -unchecked' defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@627bcd7e o2: Outer = Outer@70543cae scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = true vs scala> class Outer { case class Inner(n: Int) } defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@77ba583 o2: Outer = Outer@5613247e scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = false This isn't a problem in the REPL even with its class-based wrappers as there is only ever 1 instance of (each) wrapper class.
Case classes, such as final case classes nested within other classes, lose their outer pointer which makes their equality lie (scala/bug#4440): scala> class Outer { final case class Inner(n: Int) } warning: there was one unchecked warning; for details, enable `:setting -unchecked' or `:replay -unchecked' defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@627bcd7e o2: Outer = Outer@70543cae scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = true vs scala> class Outer { case class Inner(n: Int) } defined class Outer scala> val o1, o2 = new Outer o1: Outer = Outer@77ba583 o2: Outer = Outer@5613247e scala> new o1.Inner(1) == new o2.Inner(1) res0: Boolean = false This isn't a problem in the REPL even with its class-based wrappers as there is only ever 1 instance of (each) wrapper class.
removed possible scalac bug: scala/bug#4440
=== What steps will reproduce the problem (please be specific and use wikiformatting)? ===
The following code crashes with a
NoSuchMethodError
:The crash does not happen when one of these changes is applied:
Inner
is asealed abstract class
instead of asealed trait
val inner
is not a type projection but justInner
Inner1
is not marked finalThe
NoSuchMethodError
at runtime:=== What versions of the following are you using? ===
The text was updated successfully, but these errors were encountered: