dotnet/roslyn
在 GitHub 查看Roslyn emits invalid IL with enums that aren't expressible in C#
Open
#68,770 建立於 2023年6月26日
Area-CompilersBughelp wanted
倉庫指標
- Star
- (20,414 star)
- PR 合併指標
- (平均合併 6天 17小時) (30 天內合併 256 個 PR)
描述
Version Used: Any
Steps to Reproduce:
- 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
- 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) { }
}
- 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