c# - Implicit conversion to System.Double with a nullable struct via compiler generated locals: why is this failing? -


given following, why invalidcastexception thrown? can't see why should outside of bug (this in x86; x64 crashes 0xc0000005 in clrjit.dll).

class program {     static void main(string[] args)     {         mydouble? = new mydouble(1.0);         boolean compare = == 0.0;     }      struct mydouble     {         double? _value;          public mydouble(double value)         {             _value = value;         }          public static implicit operator double(mydouble value)         {             if (value._value.hasvalue)             {                 return value._value.value;             }              throw new invalidcastexception("mydouble value cannot convert system.double: no value present.");         }     } } 

here cil generated main():

.method private hidebysig static void main(string[] args) cil managed {     .entrypoint     .maxstack 3     .locals init (         [0] valuetype [mscorlib]system.nullable`1<valuetype program/mydouble> my,         [1] bool compare,         [2] valuetype [mscorlib]system.nullable`1<valuetype program/mydouble> cs$0$0000,         [3] valuetype [mscorlib]system.nullable`1<float64> cs$0$0001)     l_0000: nop      l_0001: ldloca.s     l_0003: ldc.r8 1     l_000c: newobj instance void program/mydouble::.ctor(float64)     l_0011: call instance void [mscorlib]system.nullable`1<valuetype program/mydouble>::.ctor(!0)     l_0016: nop      l_0017: ldloc.0      l_0018: stloc.2      l_0019: ldloca.s cs$0$0000     l_001b: call instance bool [mscorlib]system.nullable`1<valuetype program/mydouble>::get_hasvalue()     l_0020: brtrue.s l_002d     l_0022: ldloca.s cs$0$0001     l_0024: initobj [mscorlib]system.nullable`1<float64>     l_002a: ldloc.3      l_002b: br.s l_003e     l_002d: ldloca.s cs$0$0000     l_002f: call instance !0 [mscorlib]system.nullable`1<valuetype program/mydouble>::getvalueordefault()     l_0034: call float64 program/mydouble::op_implicit(valuetype program/mydouble)     l_0039: newobj instance void [mscorlib]system.nullable`1<float64>::.ctor(!0)     l_003e: stloc.3      l_003f: ldloca.s cs$0$0001     l_0041: call instance !0 [mscorlib]system.nullable`1<float64>::getvalueordefault()     l_0046: call float64 program/mydouble::op_implicit(valuetype program/mydouble)     l_004b: conv.r8      l_004c: ldc.r8 0     l_0055: bne.un.s l_0060     l_0057: ldloca.s cs$0$0001     l_0059: call instance bool [mscorlib]system.nullable`1<float64>::get_hasvalue()     l_005e: br.s l_0061     l_0060: ldc.i4.0      l_0061: stloc.1      l_0062: ret  } 

note lines 0x2d - 0x3e in il. retrieves mydouble? instance, calls getvalueordefault on it, calls implicit operator on that, , wraps result in double? , stores in compiler-generated cs$0$0001 local. in lines 0x3f 0x55, retrieve cs$0$0001 value, 'unwrap' via getvalueordefault , compare 0... but wait minute! call mydouble::op_implicit doing on line 0x46?

if debug c# program, indeed see 2 calls implicit operator double(mydouble value), , 2nd call fails, since value not initialized.

what going on here?

it c# compiler bug. bringing attention.

incidentally, bad practice have user defined implicit conversion operator throws exception; documentation states implicit conversions should never throw. sure don't want explicit conversion?

anyway, bug.

the bug repros in c# 3 , 4 not in c# 2. means fault. caused bug when redid user-defined lifted implicit operator code in order make work expression tree lambdas. sorry that! code tricky, , apparently did not test adequately.

what code supposed is:

first, overload resolution attempts resolve meaning of ==. best == operator both arguments valid lifted operator compares 2 nullable doubles. therefore should analyzed as:

boolean compare = (double?)my == (double?)0.0;  

(if write code right thing in c# 3 , 4.)

the meaning of lifted == operator is:

  • evaluate both arguments
  • if both null result true -- cannot happen in case
  • if 1 null , other not result false
  • if both not null both unwrapped double , compared doubles.

now question "what right way evaluate left hand side?"

we have here lifted user-defined conversion operator mydouble? double?. correct behaviour is:

  • if "my" null, result null double?.
  • if "my" not null result user-defined conversion of my.value double, , conversion of double double?.

clearly going wrong in process.

i'll enter bug in our database, fix miss deadline changes make next service pack. looking workarounds if you. again, apologies error.


Comments

Popular posts from this blog

c# - How to set Z index when using WPF DrawingContext? -

razor - Is this a bug in WebMatrix PageData? -

android - layout with fragment and framelayout replaced by another fragment and framelayout -