NullReferenceException in VS2015 C ++ / CLI Release Build

I get a "System.NullReferenceException: the object reference is not set to the object instance." according to my version. I created an example application that mimics what's in my production code.

void Abc::LogService::Log(String^ message) { try { int ret = DoProcessing(message); Exception^ ex; if (ret == 0) { ex = gcnew ArgumentException("Processing done."); } else { ex = gcnew ArgumentNullException("message", "Null args"); } throw ex; } finally { //do someother thing. } } 

With the above code, it reports an exception: at Abc.LogService.Log(String message) in logservice.cpp:line 19 , which corresponds to the throw ex; statement throw ex; in code.

The MSIL in the release build for this feature looks like this:

 .method public hidebysig instance void Log(string message) cil managed { // Code size 46 (0x2e) .maxstack 4 .locals ([0] class [mscorlib]System.Exception V_0, [1] class [mscorlib]System.Exception ex) .try { IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call instance int32 Abc.LogService::DoProcessing(string) IL_0007: ldnull IL_0008: stloc.1 IL_0009: brtrue.s IL_0018 IL_000b: ldstr "Processing done." IL_0010: newobj instance void [mscorlib]System.ArgumentException::.ctor(string) IL_0015: stloc.0 IL_0016: br.s IL_0028 IL_0018: ldstr "message" IL_001d: ldstr "Null args" IL_0022: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string, string) IL_0027: stloc.0 IL_0028: ldloc.1 IL_0029: throw IL_002a: leave.s IL_002d } // end .try finally { IL_002c: endfinally } // end handler IL_002d: ret } // end of method LogService::Log 

From the MSIL code, it shows that in statement IL_0028, it loads a null value and causes a throw in the following expression. The strange part is, this only happens if I have a try-finally block. Debugging builds above code works fine.

Does this sound like a bug in the VS2015 v140 kit?

+6
source share
1 answer

Yes, this is an optimizer error. Quite unusual, the first one I saw for C ++ / CLI is a language where jitter should do a heavy lift. It seems to have worked by declaring the ex variable inside the try block, causing it to suffocate from the initialization guarantee. Looks like a stream analysis error.

Shortly from compiling with / Od, one way is to move the variable from the try block

 void Log(String^ message) { Exception^ ex; try { // etc... } 

It also turns out much better than MSIL, completely eliminating the variable:

 .method public hidebysig instance void Log(string message) cil managed { // Code size 41 (0x29) .maxstack 4 .try { IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call instance int32 Test::DoProcessing(string) IL_0007: brtrue.s IL_0015 IL_0009: ldstr "Processing done." IL_000e: newobj instance void [mscorlib]System.ArgumentException::.ctor(string) IL_0013: br.s IL_0024 IL_0015: ldstr "message" IL_001a: ldstr "Null args" IL_001f: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string, string) IL_0024: throw IL_0025: leave.s IL_0028 } // end .try finally { IL_0027: endfinally } // end handler IL_0028: ret } // end of method Test::Log 

Optimizer errors suck, you can report it on connect.microsoft.com

+3
source

All Articles