dotnet/roslyn

Roslyn emits invalid IL with enums that aren't expressible in C#

Open

#68,770 建立於 2023年6月26日

在 GitHub 查看
 (3 留言) (0 反應) (0 負責人)C# (4,257 fork)batch import
Area-CompilersBughelp wanted

倉庫指標

Star
 (20,414 star)
PR 合併指標
 (平均合併 6天 17小時) (30 天內合併 256 個 PR)

描述

Version Used: Any

Steps to Reproduce:

  1. Ilasm this:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

.assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) }

.assembly UncommonEnums { }

.class public auto ansi sealed CharEnum
       extends [System.Runtime]System.Enum
{
  .field public specialname rtspecialname char value__
} // end of class CharEnum

.class public auto ansi sealed BoolEnum
       extends [System.Runtime]System.Enum
{
  .field public specialname rtspecialname bool value__
} // end of class BoolEnum

.class public auto ansi sealed FloatEnum
       extends [System.Runtime]System.Enum
{
  .field public specialname rtspecialname float32 value__
} // end of class FloatEnum

.class public auto ansi sealed DoubleEnum
       extends [System.Runtime]System.Enum
{
  .field public specialname rtspecialname float64 value__
} // end of class DoubleEnum

.class public auto ansi sealed IntPtrEnum
       extends [System.Runtime]System.Enum
{
  .field public specialname rtspecialname native int value__
} // end of class IntPtrEnum

.class public auto ansi sealed UIntPtrEnum
       extends [System.Runtime]System.Enum
{
  .field public specialname rtspecialname native uint value__
} // end of class UIntPtrEnum
  1. Compile this while referencing UncommonEnums.dll:
using System;
using System.Runtime.CompilerServices;

public static class Program
{
    public static void Main()
    {
		long a = 0;
		long b = -1;
		
        var enumA = Unsafe.As<long, SimpleEnum>(ref a);
        var enumB = Unsafe.As<long, SimpleEnum>(ref b);
        Use(enumA, enumB);
		
        var enumCharA = Unsafe.As<long, CharEnum>(ref a);
        var enumCharB = Unsafe.As<long, CharEnum>(ref b);
        Use(enumCharA, enumCharB);

        var enumBoolA = Unsafe.As<long, BoolEnum>(ref a);
        var enumBoolB = Unsafe.As<long, BoolEnum>(ref b);
        Use(enumBoolA, enumBoolB);

        var enumFloatA = Unsafe.As<long, FloatEnum>(ref a);
        var enumFloatB = Unsafe.As<long, FloatEnum>(ref b);
        Use(enumFloatA, enumFloatB);

        var enumDoubleA = Unsafe.As<long, DoubleEnum>(ref a);
        var enumDoubleB = Unsafe.As<long, DoubleEnum>(ref b);
        Use(enumDoubleA, enumDoubleB);

        var enumIntPtrA = Unsafe.As<long, IntPtrEnum>(ref a);
        var enumIntPtrB = Unsafe.As<long, IntPtrEnum>(ref b);
        Use(enumIntPtrA, enumIntPtrB);

        var enumUIntPtrA = Unsafe.As<long, UIntPtrEnum>(ref a);
        var enumUIntPtrB = Unsafe.As<long, UIntPtrEnum>(ref b);
        Use(enumUIntPtrA, enumUIntPtrB);
    }
	
	public enum SimpleEnum { }
	
	[MethodImpl(MethodImplOptions.NoInlining)]
	private static void Use<T>(T a, T b) { }
}
  1. Check the IL

Diagnostic Id:

n/a

Expected Behavior: Valid loads are generated.

Actual Behavior: ldind.ref is generated for the weird enums which then crashes the JIT with:

Assertion failed 'genActualType(lclTyp) == genActualType(op1->gtType) || (genActualType(lclTyp) == TYP_I_IMPL && op1->OperIs(GT_LCL_ADDR)) || (genActualType(lclTyp) == TYP_I_IMPL && (op1->gtType == TYP_BYREF || op1->gtType == TYP_REF)) || (genActualType(op1->gtType) == TYP_I_IMPL && lclTyp == TYP_BYREF) || (varTypeIsFloating(lclTyp) && varTypeIsFloating(op1->TypeGet())) || ((genActualType(lclTyp) == TYP_BYREF) && genActualType(op1->TypeGet()) == TYP_REF)'

Discovered in: https://github.com/dotnet/runtime/pull/88006 cc @jkotas

貢獻者指南