bug icon indicating copy to clipboard operation
bug copied to clipboard

NullPointerException when putting a Singleton type in an AnyVal

Open Atry opened this issue 6 years ago • 4 comments

Welcome to Scala 2.13.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_202-ea).
Type in expressions for evaluation. Or try :help.

scala> class Foo[S <: AnyRef](val s: S) extends AnyVal
defined class Foo

scala> def Foo[S <: AnyRef](name: S) = new Foo[name.type](name)
Foo: [S <: AnyRef](name: S)Foo[name.type]

scala> Foo("xx")
java.lang.NullPointerException
  at Foo$.hashCode$extension(<console>:1)
  at Foo.hashCode(<console>:1)
  at java.lang.Object.toString(Object.java:236)
  at scala.runtime.ScalaRunTime$.inner$1(ScalaRunTime.scala:238)
  at scala.runtime.ScalaRunTime$.stringOf(ScalaRunTime.scala:243)
  at scala.runtime.ScalaRunTime$.replStringOf(ScalaRunTime.scala:251)
  at .$print$lzycompute(<synthetic>:8)
  ... 26 elided

Atry avatar Aug 03 '19 23:08 Atry

class Foo[S <: AnyRef](val s: S) extends AnyVal

object Test {
  def Foo[S <: AnyRef](name: S) = new Foo[name.type](name)

  val foo = Foo("x")

  def main(args: Array[String]) = println {
    foo
  }
}

The foo field initialization is incorrectly elided in constructors, apparently. Null foo results in NPE.

  object Test extends Object {
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    };
    def Foo(name: Object): Object = name;
    private[this] val foo: String("x") = Test.this.Foo("x").$asInstanceOf[String("x")]();
    <stable> <accessor> def foo(): String("x") = Test.this.foo;
    def main(args: Array[String]): Unit = scala.Predef.println(new Foo(Test.this.foo()))
  }
[[syntax trees at end of              constructors]] // t11659.scala
  object Test extends Object {
    def Foo(name: Object): Object = name;
    <stable> <accessor> def foo(): String("x") = Test.this.foo;
    def main(args: Array[String]): Unit = scala.Predef.println(new Foo(Test.this.foo()));
    def <init>(): Test.type = {
      Test.super.<init>();
      ()
    }
  }

som-snytt avatar Sep 20 '21 16:09 som-snytt

I'm not familiar with this. Looks like I lost this? private[this] val foo: String("x") in object Test, tree.tpe=String("x")

I tested it. It seems that it has nothing to do with repl. It's a compiler error. The compiler did not call putstatic for field foo before use getstatic

      DefDef( // val foo(): String("x") in object Test
        <method> <stable> <accessor> <triedcooking>
        "foo"
        []
        List(Nil)
        <tpt> // tree.tpe=String("x")
        Test.this."foo " // private[this] val foo: String("x") in object Test, tree.tpe=String("x")
      )

jxnu-liguobin avatar Sep 20 '21 17:09 jxnu-liguobin

I did some interesting tests.

conclusion: lost private[this] val f: String("x") = _;,Test.this.f = Test.this.Foo("x").$asInstanceOf[String("x")](): String("x")

But I don't know where the relevant files are and whether they can be repaired? Can anyone tell me? Thank you

Singleton

 object Test extends Object {
    def Foo(name: Object): Object = name;
    <stable> <accessor> def f(): String("x") = Test.this.f;
    def main(args: Array[String]): Unit = scala.Predef.println(new sandbox.Foo(Test.this.f()));
    def <init>(): sandbox.Test.type = {
      Test.super.<init>();
      ()
    }
  }

non-Singleton

 object Test extends Object {
    def Foo(name: Object): Object = name;
    private[this] val f: String = _;
    <stable> <accessor> def f(): String = Test.this.f;
    def main(args: Array[String]): Unit = scala.Predef.println(scala.Int.box(sandbox.Foo.hashCode$extension(Test.this.f())));
    def <init>(): sandbox.Test.type = {
      Test.super.<init>();
      Test.this.f = Test.this.Foo("x").$asInstanceOf[String]();
      ()
    }
  }

Singleton but lazy

object Test extends Object {
    final <synthetic> lazy private[this] var f: String("x") = _;
    @volatile private[this] var bitmap$0: Boolean = _;
    def Foo(name: Object): Object = name;
    private def f$lzycompute(): String("x") = {
      Test.this.synchronized[Unit](if (Test.this.bitmap$0.unary_!())
        {
          Test.this.f = (Test.this.Foo("x").$asInstanceOf[String("x")](): String("x"));
          Test.this.bitmap$0 = true
        });
      Test.this.f
    };
    <stable> <accessor> lazy def f(): String("x") = (if (Test.this.bitmap$0.unary_!())
      Test.this.f$lzycompute()
    else
      Test.this.f: String("x"));
    def main(args: Array[String]): Unit = scala.Predef.println(new sandbox.Foo(Test.this.f()));
    def <init>(): sandbox.Test.type = {
      Test.super.<init>();
      ()
    }
  }

jxnu-liguobin avatar Sep 21 '21 05:09 jxnu-liguobin

Dale and I looked at the Scala 3 behavior. Scala 3 doesn't crash, but note that it avoids the bug by emitting the field and properly initializing it. That isn't wrong, strictly speaking, but emitting the field isn't strictly necessary and has costs. See https://github.com/lampepfl/dotty/issues/12570 for a lot of discussion on this. But note that that ticket doesn't bring extends AnyVal into the picture.

SethTisue avatar Sep 22 '21 15:09 SethTisue