-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Proposal: support an enum constraint on generic type parameters #262
Comments
For reference: long, too long unfortunately, discussion from codeplex: http://roslyn.codeplex.com/discussions/572464 |
Ultimately what we could distill from that is that thread is the following:
In my opinion out of the three potential CLR constraint combinations 1.iii. is the one that would be the most useful. I don't know how one would write a generic method or type accepting Unfortunately, without support for 2.i. the use cases for this feature in general are mostly for the purposes of reflection. Generally improved versions of existing CLR functions, such as parsing the Given that these scenarios largely involve reflection that does pose a small problem if compiling to .NET Native as some of the existing methods of |
I was toying with .NET Native in Visual Studio 2015 CTP6 and it seems that the issue with reflection and enums are no longer a problem as |
Another reference: http://roslyn.codeplex.com/discussions/543871 |
Will this feature be included in C# 7? |
It isn't on any of our published plans for C# 7. |
So, the Ready label means that work on this feature can start in any moment, right? |
Yes, design work on this feature can start whenever the language design team feels it is their highest priority. |
Are you sure? I'm currently using this and it works perfectly for all kinds of enums. Not sure that it covers all cases tho.
|
Hmm, so it's not valid IL to for example load Why does arithmetic require knowing the type, because of signed overflows or negative numbers?
For me, it would be incredibly helpful to have just the constraint available for C#, then I would be able to create "any" necessary methods in C# by using these two IL methods above. Without the constraint I can use these two methods, but I cannot create any other method in C#. Further, if these two methods would be directly available (or explicit So yes, the constraint have value even when arithmetic operations are not available. |
I apologize for the shameless plug but I believe this may remove most needs for adding the enum constraint. I've just released version 1.0.0 of Enums.NET, a high-performance type-safe .NET enum utility library. I've added every enum operation that I thought could be applied to all enums or flag enums. Enums.NET available on NuGet and is compatible with .NET Framework 2.0+ as well as .NET Standard 1.0+. I'd greatly appreciate any feedback on it. Thanks. |
@TylerBrinkley There's a major difference between a library and a language feature, some people cannot use 3rd-party libraries, sometimes it just looks awkward and detached from the code and so expressing it through the language result in a more legible code but otherwise, good job. :) |
@OndrejPetrzilka
I am fairly certain this code will cause some strange behaviors when the value returned is used if the value didn't fit in the enum's underlying type. I don't have a chance to test it right now, but I am fairly certain the following code will return false with FromUInt64 using your implementation because the value on the stack is never truncated.
With optimizations turned on, the following IL is generated:
Notice that there is no This means that This is because CSC expects the values of enums on the stack to always be truncated to the underlying type. This is cheaper than truncating it all of the time when it is used. For instance, look at a (non-generic) implementation of FromUInt64 (
Notice, the conversion happens immediately. It doesn't put the non-truncated version of Nevermind, I went braindead when I wrote the bit below.
Overflows in general, yeah. TL;DR: Your code doesn't cover all the edge cases. |
@PathogenDavid EDIT: I'm wrong here, I talk about I just wrote a unit test for the case you mentioned and it passes (x86/x64, release/debug). My idea was that it should work, because return value is passed by value, thus it copies only 16 bits from ulong when returning the value. I have no idea why it works on release with aggressive inlining, maybe the method is inlined by JIT, but it's clever enough to detect that it should still take just the lower bits? Or it's stored in 64-bit register anyway and when doing operations on it (comparison to ShortEnum.BitZero), it uses instruction which works on lowest 16 bits? (I have no understanding of x86/x64 assembler).
I think this use is okay, converting any numeric value on stack to ulong should be fine, no matter what. Here's another test which passes too. It proves that in case you mentioned, return value is same as returned by direct cast. It's not 0x8001 though, I don't know why it should, but as far as I'm concerned, important thing is that it casts same as direct cast.
|
@OndrejPetrzilka As for everything else...
Good point, I'm not sure how CLR handles this. I would imagine it just uses EAX for the return value, but maybe it is smart enough to truncate it for you if it thinks/knows it needs to be. However, the fact that my release-mode
I would bet this is what is happening. I bet the JITter generates a 16-bit compare for the comparison since it knows However, I'd consider this getting into undefined JIT behavior. I'd be worried about how this would behave on other architectures (which may or may not support 16-bit compares) or more naive JIT implementations (that just use word-size-compares everywhere.) |
Such IL is unverifiable. It's not clear if it's also invalid, the ECMA spec doesn't say nothing about this. It happens so that the JIT compiler inserts a cast automatically in this case.
Yes, you can do that but it's not 100% correct. If the underlying type of the enum is signed then you need to use
That test should fail.
I'm not sure what you mean by "works on release with aggressive inlining". Aggressive inlining has no effect in this case, the inliner always rejects |
ToUInt64Sorry for confusion, you were indeed right, Does FromUInt64
I had no idea that it works this way. Is this IL better for conversion from
|
I don't think so. Try an enum having underlying type
Depends on what you mean by "better":
|
You're right, this test fails
I was concerned about second point (verifiable/correct). How's that different from making union in C# (explicit struct layout with field offsets), because I believe that it's verifiable/correct when it contains no reference types. In worst case (performance wise), I can implement similar approach in IL as well to make it verifiable. Or union wouldn't work in IL, because it does not allow generic type in it? Lets say I don't care about cast to In such case I would get "wrong" number, but I'd be able to apply bit operations correctly and cast it back to enum (if we figure out verifiable way). Bit operations would be: And, Or, Xor, Not, Ceq. Since there's no Add/Sub, I don't have to care about overflow and conversions. Or we can skip GetBits/SetBits completely and just implement bit operations in type-independent way. Because I think this is perfectly valid IL:
I just though it would be nice to have a way of getting/setting all the bits at once as |
Based on above, it says it's valid when types are assignment-compatible. EDIT: it's not verified |
Yep, you can't have a generic type that has explicit layout:
|
Assigning back to champion, as feature is now complete. |
Addressed as part of C# 7.3, as described in dotnet/csharplang#104 For VB, I am not aware of any outstanding issue. |
@gafter This VB issue was added recently: dotnet/vblang#306 |
Allow the constraints on a type parameter to include
enum
. Such a constrained type parameter could be instantiated with an enum type or with another type parameter that is so constrained.This probably needs more design work.
The text was updated successfully, but these errors were encountered: