dotnet/roslyn

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

Open

#68.770 geöffnet am 26. Juni 2023

Auf GitHub ansehen
 (3 Kommentare) (0 Reaktionen) (0 zugewiesene Personen)C# (4.257 Forks)batch import
Area-CompilersBughelp wanted

Repository-Metriken

Stars
 (20.414 Stars)
PR-Merge-Metriken
 (Durchschn. Merge 6T 17h) (256 gemergte PRs in 30 T)

Beschreibung

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

Contributor Guide