Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
MichalPetryka opened this issue Jun 26, 2023 · 3 comments
Open

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

MichalPetryka opened this issue Jun 26, 2023 · 3 comments
Labels
Area-Compilers Bug help wanted The issue is "up for grabs" - add a comment if you are interested in working on it
Milestone

Comments

@MichalPetryka
Copy link

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: dotnet/runtime#88006
cc @jkotas

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jun 26, 2023
@333fred
Copy link
Member

333fred commented Jun 26, 2023

This is all undefined behavior in C#. Imo we should just mark such enum types as bad on import and prohibit usage.

@jcouv jcouv added the Bug label Jun 26, 2023
@jcouv jcouv added this to the Compiler.Next milestone Jun 26, 2023
@jcouv jcouv added help wanted The issue is "up for grabs" - add a comment if you are interested in working on it and removed untriaged Issues and PRs which have not yet been triaged by a lead labels Jun 26, 2023
@jaredpar jaredpar modified the milestones: Compiler.Next, Backlog Sep 12, 2023
@Zastai
Copy link

Zastai commented Dec 2, 2023

I'm assuming the last two are fine now that nint and nuint are valid integral types?

(If they're still invalid, then the documentation is wrong, because it says all integral types are valid bases, linking to this page which lists nint and nuint).

@hez2010
Copy link

hez2010 commented Feb 23, 2025

nint and nuint are valid enum underlying types as per ECMA 335:

II.14.3 Enums
An enum (short for enumeration) defines a set of symbols that all have the same type. A type shall be an enum if and only if it has an immediate base type of System.Enum. Since System.Enum itself has an immediate base type of System.ValueType, (see Partition IV) enums are value types (§II.13) The symbols of an enum are represented by an underlying integer type: one of { bool, char, int8, unsigned int8, int16, unsigned int16, int32, unsigned int32, int64, unsigned int64, native int, unsigned native int }

While float and double are not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Bug help wanted The issue is "up for grabs" - add a comment if you are interested in working on it
Projects
None yet
Development

No branches or pull requests

6 participants