为什么发生CLR异常致命执行引擎错误

本文关键字:执行 引擎 错误 异常 CLR 为什么 | 更新日期: 2023-09-27 18:19:25

我们使用的是一个封装数值的结构,我发现当在表达式中使用该结构的可为null版本时,会发生FatalExecutionEngineError

附加信息:运行时遇到致命错误。这个错误地址位于线程0x52d8上的0x729c1e04。错误代码为0xc0000005。此错误可能是CLR或用户代码的不安全或不可验证的部分。这方面的共同来源错误包括COM互操作或PInvoke的用户封送处理错误可能会损坏堆栈。

我正在使用Visual Studio Premium 2013 Update 3以下是源代码(C#,目标.NET Framework 4.5):

using System;
using System.Globalization;
namespace ConsoleApplication4
{
    public struct Number
    {
        ValueType _val;
        private Number(double val)
        {
            this._val = val;
        }
        public static implicit operator double(Number val)
        {
            return Convert.ToDouble(val._val, CultureInfo.InvariantCulture);
        }
        public static implicit operator Number(double val)
        {
            return new Number(val);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Number? b = 1.2;
            var c = b - 1.2;
            Number b1 = 1.2;
            var c1 = b1 - 1.2;
        }
    }
}

请注意,添加此项可以解决问题,因此并不紧急,但我非常感兴趣的是,为什么会出现此问题。

public static implicit operator double(Number? val)
{
    return Convert.ToDouble(val.GetValueOrDefault()._val, CultureInfo.InvariantCulture);
}

为什么发生CLR异常致命执行引擎错误

这看起来像是一个编译器错误。问题发生在Main的第二行。

        Number? b = 1.2;
        var c = b - 1.2; // The problem lies here

请注意VS2013生成的IL,问题在于IL_005C和被包围的代码,这是不必要的生成:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       116 (0x74)
  .maxstack  2
  .locals init (valuetype [mscorlib]System.Nullable`1<valuetype Test.Number> V_0,
           valuetype [mscorlib]System.Nullable`1<float64> V_1,
           valuetype [mscorlib]System.Nullable`1<valuetype Test.Number> V_2,
           valuetype [mscorlib]System.Nullable`1<float64> V_3,
           valuetype [mscorlib]System.Nullable`1<float64> V_4)
// Number? b = 1.2;
  IL_0000:  nop
  IL_0001:  ldloca.s   V_0 //b
  IL_0003:  ldc.r8     1.2
  IL_000c:  call       valuetype Test.Number Test.Number::op_Implicit(float64)
  IL_0011:  call       instance void valuetype [mscorlib]System.Nullable`1<valuetype Test.Number>::.ctor(!0)
  IL_0016:  nop
  IL_0017:  ldloc.0
  IL_0018:  stloc.2    // b
// var c = b - 1.2;
  IL_0019:  ldloca.s   V_2 // b
  IL_001b:  call       instance bool valuetype [mscorlib]System.Nullable`1<valuetype Test.Number>::get_HasValue()
  IL_0020:  brtrue.s   IL_002d
  IL_0022:  ldloca.s   V_3
  IL_0024:  initobj    valuetype [mscorlib]System.Nullable`1<float64>
  IL_002a:  ldloc.3
  IL_002b:  br.s       IL_003e
  IL_002d:  ldloca.s   V_2
  IL_002f:  call       instance !0 valuetype [mscorlib]System.Nullable`1<valuetype Test.Number>::GetValueOrDefault()
  IL_0034:  call       float64 Test.Number::op_Implicit(valuetype Test.Number)
  // Um, what? First part of compiler bug is that it's needlessly creating a nullable float
  IL_0039:  newobj     instance void valuetype [mscorlib]System.Nullable`1<float64>::.ctor(!0)
  IL_003e:  nop
  IL_003f:  stloc.3
  IL_0040:  ldloca.s   V_3
  IL_0042:  call       instance bool valuetype [mscorlib]System.Nullable`1<float64>::get_HasValue()
  IL_0047:  brtrue.s   IL_0055
  IL_0049:  ldloca.s   V_4
  IL_004b:  initobj    valuetype [mscorlib]System.Nullable`1<float64>
  IL_0051:  ldloc.s    V_4
  IL_0053:  br.s       IL_0071
  IL_0055:  ldloca.s   V_3
  // Here's the real bug, though.  It's passing float64 to a the op_Implicit that is expecting a Number struct
  IL_0057:  call       instance !0 valuetype [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
  IL_005c:  call       float64 Test.Number::op_Implicit(valuetype Test.Number)
  IL_0061:  conv.r8

  IL_0062:  ldc.r8     1.2
  IL_006b:  sub
  IL_006c:  newobj     instance void valuetype [mscorlib]System.Nullable`1<float64>::.ctor(!0)
  IL_0071:  nop
  IL_0072:  stloc.1
  IL_0073:  ret
} // end of method Program::Main

Roslyn(也是使用它的VS14CTP)产生了不同的IL,但没有这个问题。该代码几乎相同,只是省略了IL_0039IL_0061之间的IL。