Scala by-name parameter with default null throws NullPointerException -


the following snippet throws nullpointerexception. expected , normal behavior of scala?

object atest extends app {     def getx[t <: x](constr: ⇒ t = null.asinstanceof[t]): unit = {         constr     }     getx() } class x 

generated (decompied) java code snippet:

public final class atest { public static void main(string[] arrstring) {     atest$.module$.main(arrstring); } public static void delayedinit(function0<boxedunit> function0) {     atest$.module$.delayedinit(function0); } public static string[] args() {     return atest$.module$.args(); } public static void scala$app$_setter_$executionstart_$eq(long l) {     atest$.module$.scala$app$_setter_$executionstart_$eq(l); } public static long executionstart() {     return atest$.module$.executionstart(); } public static void delayedendpoint$test$atest$1() {     atest$.module$.delayedendpoint$test$atest$1(); } public static <t extends x> t getx$default$1() {     return atest$.module$.getx$default$1(); } public static <t extends x> void getx(function0<t> function0) {     atest$.module$.getx(function0); } }   public final class atest$ implements app { public static final atest$ module$; private final long executionstart; private string[] scala$app$$_args; private final listbuffer<function0<boxedunit>> scala$app$$initcode;  public static {     new test.atest$(); } public long executionstart() {     return this.executionstart; } public string[] scala$app$$_args() {     return this.scala$app$$_args; } public void scala$app$$_args_$eq(string[] x$1) {     this.scala$app$$_args = x$1; } public listbuffer<function0<boxedunit>> scala$app$$initcode() {     return this.scala$app$$initcode; } public void scala$app$_setter_$executionstart_$eq(long x$1) {     this.executionstart = x$1; } public void scala$app$_setter_$scala$app$$initcode_$eq(listbuffer x$1) {     this.scala$app$$initcode = x$1; } public string[] args() {     return app.class.args((app)this); } public void delayedinit(function0<boxedunit> body) {     app.class.delayedinit((app)this, body); } public void main(string[] args) {     app.class.main((app)this, (string[])args); } public <t extends x> void getx(function0<t> constr) {     constr.apply(); } public <t extends x> t getx$default$1() {     return null; } public final void delayedendpoint$test$atest$1() {     this.getx((function0<t>)new scala.serializable(){         public static final long serialversionuid = 0;          public final nothing. apply() {             return (nothing.)atest$.module$.getx$default$1();         }     }); } private atest$() {     module$ = this;     app.class.$init$((app)this);     this.delayedinit((function0<boxedunit>)new atest.delayedinit$body(this)); } }  public final class atest$.anonfun extends abstractfunction0<nothing.>implements serializable { public final nothing. apply() {         return (nothing.)atest$.module$.getx$default$1();         } } 

and action part:

public <t extends x> void getx(function0<t> constr) {     constr.apply(); } public <t extends x> t getx$default$1() {     return null; } public final void delayedendpoint$test$atest$1() {     this.getx((function0<t>)new scala.serializable(){         public final nothing. apply() {             return (nothing.)atest$.module$.getx$default$1();         }     }); } 

that is: call getx passes new anon function0 apply() calls getx$default$1() null. can not see point npe can thrown.

edit: unresolved issue found: https://issues.scala-lang.org/browse/si-8097

edit: expression null.asinstanceof[t] generates default value type t. in case when scala infers resulting type t nothing come runtime expression

null.asinstanceof[nothing] 

that throws exeption default nothing exception.

but why snippet throws npe @ last line?

object atest extends app {     def getx[t](x: t = null.asinstanceof[t]): t = x     getx[nothing]() // ok     val x = getx() // ok      val y = null     println("x= "+x) // prints 'x= null'     println(s"y= $y") // prints 'y= null'     println(s"x= $x") // throws npe !?     println("x==null ? "+(x==null)) // prints 'x= null' } 

and why snippet throws npe (it different previous in implicit param)?

object atest extends app {     def getx[t](x: t = null.asinstanceof[t])(implicit s: string = null): t = x     getx() // throws npe !? } 

so situation still vague. , question open.

so have revise answer bit.

what triggers npe clear byte code, not reverse compiled java code. byte code has more features java code, important 1 being, can have 2 methods differ in return type , different things.

so lets first @ stack trace:

at atest$$anonfun$1.apply(test.scala:7) @ atest$.getx(test.scala:5) @ atest$.delayedendpoint$atest$1(test.scala:7) @ atest$delayedinit$body.apply(test.scala:3) @ scala.function0$class.apply$mcv$sp(function0.scala:40) @ scala.runtime.abstractfunction0.apply$mcv$sp(abstractfunction0.scala:12) @ scala.app$$anonfun$main$1.apply(app.scala:76) @ scala.app$$anonfun$main$1.apply(app.scala:76) @ scala.collection.immutable.list.foreach(list.scala:383) @ scala.collection.generic.traversableforwarder$class.foreach(traversableforwarder.scala:35) @ scala.app$class.main(app.scala:76) @ atest$.main(test.scala:3) @ atest.main(test.scala) 

so method goes wrong atest$$anonfun$1.apply

lets @ that:

public final scala.runtime.nothing$ apply(); code:    0: getstatic     #19                 // field atest$.module$:latest$;    3: invokevirtual #23                 // method atest$.getx$default$1:()lx;    6: checkcast     #25                 // class scala/runtime/nothing$    9: areturn  public final java.lang.object apply(); code:    0: aload_0    1: invokevirtual #30                 // method apply:()lscala/runtime/nothing$;    4: athrow 

the first thing notice there 2 methods called apply, 1 called (the athrow hint...) well, lets @ method calling it:

public <t extends x> void getx(scala.function0<t>); code:    0: aload_1    1: invokeinterface #62,  1  // interfacemethod scala/function0.apply:()ljava/lang/object;    6: pop    7: return 

so, calling 1 returns object , has athrow instruction. why give nullpointer exception?

well, method following: places on stack, invokes other apply method (returning nothing$), method returns null returns our default argument. , have null on stack , execute athrow. , athrow throws npe instead if finds null on stack.

so happens here.

the next question is, why happen?

well, lets @ scalac makes of after typechecking:

object atest extends anyref app {     def <init>(): atest.type = {       atest.super.<init>();       ()     };     def getx[t <: x](constr: => t = null.asinstanceof[t]): unit = {       constr;       ()     };     <synthetic> def getx$default$1[t <: x]: t = null.asinstanceof[t];     atest.this.getx[nothing](atest.this.getx$default$1[nothing])   } 

and in case without asinstanceof:

object atest extends anyref app {     def <init>(): atest.type = {       atest.super.<init>();       ()     };     def getx[t <: x](constr: => t = null): unit = {       constr;       ()     };     <synthetic> def getx$default$1[t <: x]: null = null;     atest.this.getx[null](atest.this.getx$default$1[nothing])   } 

well, somehow information, default parameter null lost in first case.

in second case byte code critical method:

public final java.lang.object apply(); code:    0: aload_0    1: invokevirtual #27                 // method apply:()lscala/runtime/null$;    4: pop    5: aconst_null    6: areturn 

so here, compiler knows, argument null, , generates code box null using class null$.

what should happen?

well, not null pointer exception, sure. why compiler generate athrow in first place? because of asinstanceof[t] becomes asinstanceof[nothing] should throw exception if invoked on null.

let's try, happens if in repl:

"".asinstanceof[nothing] java.lang.classcastexception: java.lang.string cannot cast scala.runtime.nothing$ 

so far good, , this:

null.asinstanceof[nothing] java.lang.nullpointerexception 

well, maybe should have started this... seems, code generation asinstanceof has bug , throws wrong exception.

why lower bound :> null fixes problem, clear: inferred type no longer nothing null , instanceof fine.

so more interesting problem why type checker fails on complex example have removed now.

complex example

class x object atest extends app {   def getx[t<:x](clas: class[t], constr: ⇒ t = null): t ={     val x = constr     if (x == null) clas.newinstance() else x   }   val clas: class[_ <: x] = classof[x]   getx(clas) // ooops: type mismatch.. } 

well, type checker say:

def getx[t <: x](clas: class[t], constr: => t = null): t = {   val x: t = constr;     if (x.==(null))       clas.newinstance()     else       x   };   <synthetic> def getx$default$2[t <: x]: null = null;   private[this] val clas: class[_ <: x] = classof[x];   <stable> <accessor> def clas: class[_ <: x] = atest.this.clas;   atest.this.getx[t](<clas: error>, atest.this.getx$default$2) } 

somehow cant infer type t, should infer null, because there classes reference types. interestingly, compiler not know that. seems directly use definition class java , type parameter there has no lower bound (because java has no type null), lower bound nothing. tells us, how fix it:

val clas: class[_ >: null <: x] = classof[x] getx(clas) 

this works. can exactly, wanted in first place. have tell compiler, not interested in classes types cannot nulled.

i think still prefer version option though:

def getx[t <: x](clas: class[t], constr: ⇒ option[t] = none): t = {   val x = constr    x match {     case none => clas.newinstance()     case some(x) => x   } } val clas: class[_ <: x] = classof[x] getx(clas) 

its clear, why works: none option[nothing], code can handle class[nothing] fine.


Comments

Popular posts from this blog

routing - AngularJS State management ->load multiple states in one page -

python - GRASS parser() error -

Swift game error message -