Enum and Delegate as Generic Type Constraints Proposal

Topics: C# Language Design, VB Language Design
Nov 19, 2014 at 1:42 AM
Edited Nov 19, 2014 at 5:23 PM

Enum and Delegate Generic Type Constraints Proposal

I'm going to try to keep this relatively simple, first outlining the syntax changes and then adding additional posts for use-cases so that others may contribute. I'm also going to try to keep this short and sweet hoping that members of the design team can help us to evolve this process.

A frequently requested feature of the C# language is lifting the imposed limitation preventing classes and methods from defining generic types constrained to the base CLR classes System.Enum or System.Delegate. The CLR doesn't impose this limitation and C# is already capable of consuming generic types and methods that declare these constraints.

Implementation

This proposal aims to amend §10.1.5 of the C# 5.0 specification by expanding the primary-constraint as follows:
primary-constraint:
    class-type
    class
    struct
    + enum
    + delegate

Enum Generic Type Constraint

If enum is specified as the primary constraint then no other constraints may be defined. This results in a type constraint of System.Enum being emitted in the IL. The struct constraint is automatically applied forcing the generic type parameter to be of an actual enum type and not of the base System.Enum type.
private static bool HasFlag(T value, T flag) where T : enum { ... }
converted to IL:
.method private hidebysig static bool  HasFlag<valuetype (System.Enum) T>(!!T value, !!T flag) cil managed
Instances of the generic type have a base type of System.Enum and can invoke instance members of that type.
public static bool HasFlag<T>(T value, T flag) where T : enum {
    return value.HasFlag(flag);
}
Instances of the generic type may be explicitly cast to and from any of the integral types, byte, sbyte, short, ushort, int, uint, long or ulong. Note that this would not prevent truncation of the underlying type of the enum is larger than the target type or vice versa.
public static int GetValue<T>(T value) where T : enum {
    return (int)value;
}
Additionally the equality operators (== and !=) and logical operators (&, |, ^ and ~) can be applied to instances of the same generic type parameter.
public static bool HasFlag<T>(T value, T flag) where T : enum {
    return (value & flag) == flag;
}

Delegate Type Constraints

If delegate is specified as the primary constraint then no other constraints may be specified. Instances of the generic type may be treated as if they are the System.Delegate type. They cannot be directly created using =, +, -, += or -= except using another instance of the same type. They also cannot be directly invoked except through the DynamicInvoke method common to the System.Delegate type.
public static T Combine<T>(T source, T target) where T : delegate {
    return source + target;
}

public static T Remove<T>(T source, T target) where T : delegate {
    return source - target;
}

public static void Combine<T>(ref T source, T target) where T : delegate {
    source += target;
} 

public static void Remove<T>(ref T source, T target) where T : delegate {
   source -= target;
}

public static objectInvoke<T>(T d, params object[] args) {
    if (d != null) return d.DynamicInvoke(args);
    return null;
}

Additional Thoughts

The F# programming language also adds support for generic type constraints specifying the underlying type of the enum or a signature for a delegate. However it does not appear that the CLR supports these constraints and that F# does not embed them in any form into the emitted assembly. As such they are only enforced by the F# compiler within the assembly.

Here is the list of supported generic constraints by the F# compiler:

F# Constraints

If the support for all of these constraint types could be added to the CLR I think that this would solve a number of the other problem cases mentioned on these forums, such as the ability to declare generic methods that call operators on the parameters.
Nov 19, 2014 at 1:49 AM

Use Cases

Enum Type Constraints

Enhanced parsing of the enum in a type-safe manner:
DateTimeKind dtk = EnumParser.Parse<DateTimeKind>("Unspecified");
AttributeTargets at = EnumParser.Parse<AttributeTargets>("Method | Property");

ConsoleColor c;
if (EnumParser.TryParse<ConsoleColor>("0")) { ... }
Improved flags detection:
AttributeTargets at = AttributeTargets.Method;
if (EnumHelper.HasFlags(at, AttributeTargets.Method)) { ... }
Nov 19, 2014 at 1:59 AM

Use Cases

Delegate Type Constraints

Helper methods to subscribe and unsubscribe events in manual event accessor methods. The C# and VB.NET compilers both produce non-trivial code in order to ensure that the subscription is done in a thread-safe manner. Rewriting that code in each accessor is error-prone. Also, the methods on Delegate all take and return other instances of Delegate requiring manual casting and preventing writing a reusable form of these methods.

The following code is not thread-safe, it is possible for two consumers to attempt to subscribe at the same time and one of those subscriptions lost when the delegate is overwritten.
public class MyClass {
    private EventHandler handler;

    public event EventHandler MyEvent {
        add { handler += value; }
        remove { handler -= value; }
    }
}
A simple helper method can be written to safely subscribe and unsubscribe handlers employing the same logic used by the C# and VB.NET compilers:
public class MyClass {
    private EventHandler handler;

    public event EventHandler MyEvent {
        add { DelegateHelper.Combine<EventHandler>(ref handler, value); }
        remove { DelegateHelper.Remove<EventHandler>(ref handler, value); }
    }
}
Coordinator
Nov 19, 2014 at 3:33 AM
Thanks for this Halo_Four! I like your start. I have a bunch of questions about enum.

Q1. What is the type of T? Is it simply that T is some type that inherits from System.Enum but is not itself System.Enum? Is "T" known to be a value type? (hence allowing you to write T? for a nullable form of it?) What is the effective base class ($10.1.5)? Presumably it's guaranteed to satisfy "typeof(T).IsEnum"?

I guess that now you've given the syntax, the next step is to give the "static semantics" (i.e. type-checking rules).

Q2. If x is an expression of type T, what do the member lookup rules say about members of x? as in "x.a" ? ($7.4)

Q3. How is this thing exported into IL?

Q4. How does F# encode its enum constraints in IL? Will C# meta-import them? How will F# meta-import the ones defined in C#?

It's okay for the language to express more stuff than metadata does. We did that with "dynamic" for instance. Look out it's encoded in metadata. It all boils down to just "object" with a set of attributes. You could very reasonably go down the same route for C# (especially if that's how F# encodes it).

Q5. Presumably your enum proposal satisfies point 4 of http://www.gamasutra.com/blogs/PedroGuida/20140113/208518/Regarding_the_future_of_C_.php

Q6. Is there any use-case other than "HasFlag" which doesn't involve reflection?

Q7. Are there any complications from the fact that the numeric literal 0 has an implicit conversion to enum? I don't see any.
Nov 19, 2014 at 10:20 AM
Halo_Four wrote:
A frequently requested feature of the C# language is lifting the imposed limitation preventing classes and methods from defining generic types constrained to the base CLR classes System.Enum or System.Delegate. The CLR doesn't impose this limitation and C# is already capable of consuming generic types and methods that declare these constraints.
This deserves some clarification. The CLI spec does indeed show that System.Enum is a valid base class constraint. It also describes how this particular base constraint behaves when combined with one of the special-purpose constraints: class, valuetype, .ctor. Of interest is the combination with valuetype, the spec claims that this constrains the parameter to be "any enum type" but not the class Enum. Without the valuetype constraint you get both "any enum type and System.Enum" which is probably not the goal here, there's little chance you'll get the 2nd and 3rd example to work if the parameter is System.Enum.
It is implied that the generic type implements the common interfaces for an enum, namely IComparable, IFormattable and IConvertible.
Since this is implied it shouldn't be mentioned at all. Otherwise people may jump in an say, hey, why don't you simply use those interfaces as constraint? If the constraint is enum you obviously get an enum value which implements whatever interfaces the System.Enum base class implements.

Also, the example provided here is a bit unfortunate. That particular ToString overload is obsolete and the "G" specifier is the default. That is, the whole thing is equivalent to x.ToString(). One could ask what's the point of this, how does this constraint help - 'struct' would work just as well.
Additionally the equality operators (== and !=) and logical operators (&, | and ^) can be applied to instances of the same generic type parameter.
Hmm, does this mean that you can use & | and ^ on enums that do not have the Flags attribute? Some people my raise concerns over this. But what's really interesting here is the implementation, see below.
Delegate Type Constraints
Do we really need to use "delegate"? It seems to me that simply allowing something like "where T : System.Delegate" is enough, unlike for enum there's no reason to block the Delegate class itself.

I would also suggest keeping these 2 constraint suggestions separate. Implementing the enum constraint can be problematic but the delegate constraint appears to be trivial. Keeping them separate would avoid having to shot down one because of the other.

Now, regarding implementation. It's unclear to what extent the CLR supports System.Enum constraints. For example, the following IL compiles and runs but peverify complains about it:
.class auto ansi sealed nested private Foo extends [mscorlib]System.Enum
{
    .field public specialname rtspecialname int64 value__
    .field public static literal valuetype Program/Foo A = int64(0x0)
    .field public static literal valuetype Program/Foo B = int64(0x42)
}

.method private hidebysig static int32 GetValue<valuetype ([mscorlib]System.Enum) T>(!!T x) cil managed
{
    .maxstack 1
    ldarg.0
    conv.i4
    ret
}

.method private hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack  1
    ldc.i8 42
    call int32 Program::GetValue<valuetype Program/Foo>(!!0)
    call void [mscorlib]System.Console::WriteLine(int32)
    ret
}
PeVerify says:
[IL]: Error: [test.exe : Program::GetValue[T]][offset 0x00000001][found (unboxed) 'T'] Expected numeric type on the stack.
1 Error(s) Verifying test.exe
A non generic version of GetValue generates the same IL code and PeVerify accepts it. It would seem that verification doesn't understand System.Enum constraints. Anyway, it runs and hopefully verification is something that can be fixed.

Supporting operators is quite interesting. It might just work due to CIL particularities:
  • instructions like and and or are partially generic - they work with both 32 and 64 bit integers (this covers the case of enum with int/uint/long/ulong underlying type)
  • integers smaller than 32 bits are extended to 32 bit on the evaluation stack (this covers the case of byte/sbyte/short/ushort underlying type)
IL like the following appears to work fine (the Foo enum has short as underlying type):
.method private hidebysig static !!T AddFlag<valuetype ([mscorlib]System.Enum) T>(!!T x, !!T y) cil managed
{
    .maxstack 2
    ldarg.0
    ldarg.1
    or
    ret
}

.method private hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 2
    ldc.i4 42
    ldc.i4 1
    call !!0 Program::AddFlag<valuetype Program/Foo>(!!0, !!0)
    call void [mscorlib]System.Console::WriteLine(int32)
    ret
}
There's a problem with supporting ~ (if you want it, you didn't include it but it seems normal to have ~ if you also have &, | and ^). A non generic version of AddFlag would have a conv.i2 before ret - it's used to ensure that the upper 16 bits of the returned 32 bit values are 0. Anding/oring/xoring 16 bit values won't introduce ones in the upper 16 bits but ~ will. And there's no way for the compiler to introduce a conv instruction in the generic version because it doesn't know the underlying type. I suspect that the JIT may solve this, it knows that the return type is a 16 bit enum so it could introduce the necessary conv by itself.

Of course, verification complains about this code too:
[IL]: Error: [test.exe : Program::AddFlag[T]][offset 0x00000002][found (unboxed) 'T'] Expected I, I4, or I8 on the stack.
Nov 19, 2014 at 12:54 PM
mdanes wrote:
Halo_Four wrote:
A frequently requested feature of the C# language is lifting the imposed limitation preventing classes and methods from defining generic types constrained to the base CLR classes System.Enum or System.Delegate. The CLR doesn't impose this limitation and C# is already capable of consuming generic types and methods that declare these constraints.
This deserves some clarification. The CLI spec does indeed show that System.Enum is a valid base class constraint. It also describes how this particular base constraint behaves when combined with one of the special-purpose constraints: class, valuetype, .ctor. Of interest is the combination with valuetype, the spec claims that this constrains the parameter to be "any enum type" but not the class Enum. Without the valuetype constraint you get both "any enum type and System.Enum" which is probably not the goal here, there's little chance you'll get the 2nd and 3rd example to work if the parameter is System.Enum.
That is interesting. So the CLR supports two versions of enum constraints. I agree, the use-cases that I think about would likely involve the combination with struct so that the parameter type is treated specifically as the type of the enum and not as System.Enum itself. The combination with new() makes sense but I also think that such probably should be implied. I'm curious if anyone else has use-cases where working directly against System.Enum would be useful enough to require additional syntax to support the two.
It is implied that the generic type implements the common interfaces for an enum, namely IComparable, IFormattable and IConvertible.
Since this is implied it shouldn't be mentioned at all. Otherwise people may jump in an say, hey, why don't you simply use those interfaces as constraint? If the constraint is enum you obviously get an enum value which implements whatever interfaces the System.Enum base class implements.
I've heard that argument before. Of course those interfaces apply to quite a few other types, including all of the C# "primitive" types, so specifying only those constraints is not constraining enough. For enum-specific examples you'd still have to check Type.IsEnum and throw otherwise. The point of supporting enum is to avoid that.
Also, the example provided here is a bit unfortunate. That particular ToString overload is obsolete and the "G" specifier is the default. That is, the whole thing is equivalent to x.ToString(). One could ask what's the point of this, how does this constraint help - 'struct' would work just as well.
Agreed, those examples weren't intended to be use-cases, just demonstrations of what I think should be valid/legal code. I think any code example demonstrating the fact that the generic type parameter implements those interfaces would be more general than to just enum.
Additionally the equality operators (== and !=) and logical operators (&, | and ^) can be applied to instances of the same generic type parameter.
Hmm, does this mean that you can use & | and ^ on enums that do not have the Flags attribute? Some people my raise concerns over this. But what's really interesting here is the implementation, see below.
I don't see why not. Neither the C# compiler nor the runtime care if you apply those operators to non-flags enums today.
Delegate Type Constraints
Do we really need to use "delegate"? It seems to me that simply allowing something like "where T : System.Delegate" is enough, unlike for enum there's no reason to block the Delegate class itself.
I went with enum and delegate primarily because it is relatively rare for a developer to refer to the class names System.Enum and System.Delegate directly. The use of the keywords in such a fashion also doesn't represent valid code today which could lead to any breaking changes. I do intend for the two keywords to represent the types as aliases and I personally wouldn't care if the decision was made to use System.Enum and System.Delegate instead of or in addition to the keywords.
I would also suggest keeping these 2 constraint suggestions separate. Implementing the enum constraint can be problematic but the delegate constraint appears to be trivial. Keeping them separate would avoid having to shot down one because of the other.
That's probably true, they're just often brought up together and I figured individually there isn't a lot of meat on their bones. I just wanted to really kick off the conversation and I'm hoping that advice from the language teams can help mold these proposals into something that can be championed by the team.
Now, regarding implementation. It's unclear to what extent the CLR supports System.Enum constraints. For example, the following IL compiles and runs but peverify complains about it:
.class auto ansi sealed nested private Foo extends [mscorlib]System.Enum
{
    .field public specialname rtspecialname int64 value__
    .field public static literal valuetype Program/Foo A = int64(0x0)
    .field public static literal valuetype Program/Foo B = int64(0x42)
}

.method private hidebysig static int32 GetValue<valuetype ([mscorlib]System.Enum) T>(!!T x) cil managed
{
    .maxstack 1
    ldarg.0
    conv.i4
    ret
}

.method private hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack  1
    ldc.i8 42
    call int32 Program::GetValue<valuetype Program/Foo>(!!0)
    call void [mscorlib]System.Console::WriteLine(int32)
    ret
}
PeVerify says:
[IL]: Error: [test.exe : Program::GetValue[T]][offset 0x00000001][found (unboxed) 'T'] Expected numeric type on the stack.
1 Error(s) Verifying test.exe
A non generic version of GetValue generates the same IL code and PeVerify accepts it. It would seem that verification doesn't understand System.Enum constraints. Anyway, it runs and hopefully verification is something that can be fixed.

Supporting operators is quite interesting. It might just work due to CIL particularities:
  • instructions like and and or are partially generic - they work with both 32 and 64 bit integers (this covers the case of enum with int/uint/long/ulong underlying type)
  • integers smaller than 32 bits are extended to 32 bit on the evaluation stack (this covers the case of byte/sbyte/short/ushort underlying type)
IL like the following appears to work fine (the Foo enum has short as underlying type):
.method private hidebysig static !!T AddFlag<valuetype ([mscorlib]System.Enum) T>(!!T x, !!T y) cil managed
{
    .maxstack 2
    ldarg.0
    ldarg.1
    or
    ret
}

.method private hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 2
    ldc.i4 42
    ldc.i4 1
    call !!0 Program::AddFlag<valuetype Program/Foo>(!!0, !!0)
    call void [mscorlib]System.Console::WriteLine(int32)
    ret
}
There's a problem with supporting ~ (if you want it, you didn't include it but it seems normal to have ~ if you also have &, | and ^). A non generic version of AddFlag would have a conv.i2 before ret - it's used to ensure that the upper 16 bits of the returned 32 bit values are 0. Anding/oring/xoring 16 bit values won't introduce ones in the upper 16 bits but ~ will. And there's no way for the compiler to introduce a conv instruction in the generic version because it doesn't know the underlying type. I suspect that the JIT may solve this, it knows that the return type is a 16 bit enum so it could introduce the necessary conv by itself.

Of course, verification complains about this code too:
[IL]: Error: [test.exe : Program::AddFlag[T]][offset 0x00000002][found (unboxed) 'T'] Expected I, I4, or I8 on the stack.
That is interesting. I was thinking of the common use cases with normal enums and I didn't think that perhaps the verifier would have issues with it. I'm a little surprised that the first example doesn't require a conv.i2 but I guess the stack would only really contain int32 or int64 anyway.
Nov 19, 2014 at 1:19 PM
lwischik wrote:
Thanks for this Halo_Four! I like your start. I have a bunch of questions about enum.

Q1. What is the type of T? Is it simply that T is some type that inherits from System.Enum but is not itself System.Enum? Is "T" known to be a value type? (hence allowing you to write T? for a nullable form of it?) What is the effective base class ($10.1.5)? Presumably it's guaranteed to satisfy "typeof(T).IsEnum"?
I replied to mdanes first because of his comment about this. Apparently the CLR supports constraints for both mechanisms separately by combining System.Enum with either class or struct constraints. The former allows the generic type parameter to actually be System.Enum whereas the latter requires that the generic type parameter be only a valid enum type. My opinion is that only the latter really makes sense in generic scenarios but I don't want to completely close the door to it until others chime in. As such, yes, T would be known to be a value type and could be nullable. It is also safely guaranteed to satisfy typeof(T).IsEnum.
I guess that now you've given the syntax, the next step is to give the "static semantics" (i.e. type-checking rules).

Q2. If x is an expression of type T, what do the member lookup rules say about members of x? as in "x.a" ? ($7.4)
I think they should work the same as when dealing with any instance of an enum. You get the instance members of System.Enum which includes the implementations of IComparable, IConvertible and IFormattable, plus the instance methods GetTypeCode and HasFlag.
Q3. How is this thing exported into IL?
private static T AddFlag<T>(T x, T y) where T : enum { ... }
into
.method private hidebysig static !!T AddFlag<valuetype ([mscorlib]System.Enum) T>(!!T x, !!T y) cil managed

and

private static void Combine<T>(ref T handler, T value) where T : delegate { ... }
into
.method private hidebysig static void Combine<(System.Delegate) T>(!!T& handler, !!T value) cil manager

mdanes has done some further tests where it comes to using arithmetic with the enum constraint which is interesting.
Q4. How does F# encode its enum constraints in IL? Will C# meta-import them? How will F# meta-import the ones defined in C#?
I took the sample code demonstrating all of the F# constraints and compiled it and then ildasmed the assembly. As far as I can tell F# doesn't encode the constraints at all so they are lost at runtime.
It's okay for the language to express more stuff than metadata does. We did that with "dynamic" for instance. Look out it's encoded in metadata. It all boils down to just "object" with a set of attributes. You could very reasonably go down the same route for C# (especially if that's how F# encodes it).
This would be an interesting road to go down considering that it potentially opens a lot of possibilities. In my opinion I would prefer that the CLR itself be amended to support additional constraints since that would set the expectation across all of the languages as well as (hopefully) ensure support from tooling like PEVerify.exe.
Q5. Presumably your enum proposal satisfies point 4 of http://www.gamasutra.com/blogs/PedroGuida/20140113/208518/Regarding_the_future_of_C_.php
Yep, that is exactly the problem I want to solve.
Q6. Is there any use-case other than "HasFlag" which doesn't involve reflection?
My use-cases probably all get into at least a little bit of reflection. I understand the concern with this and .NET Native. As I mentioned the only issue that I ran into was the fact that Enum.GetValues() throws an exception. This is because the runtime itself uses reflection code that is unsafe with .NET Native, specifically calling Array.UnsafeCreateInstance. The values are still accessible, the programmer is just required to jump through more hoops in order to obtain them.
Q7. Are there any complications from the fact that the numeric literal 0 has an implicit conversion to enum? I don't see any.
I don't either. Enums already allow you to be relatively willy-nilly with their values outside of generic programming. I think explicit casting to and from the instance of the enum and any of the integral types should be supported, as that is currently supported when working in non-generic code. This includes casting from an enum type with an underlying type of ulong to a variable of short, which is perfectly legal today and results in truncation.
Nov 19, 2014 at 3:05 PM
Halo_Four wrote:
So the CLR supports two versions of enum constraints.
Actually there are 3 versions but the 3rd isn't very useful - if you combine the class special constraint with the System.Enum base class constraint you basically say that the generic parameter can only be System.Enum and not an actual enum.
I don't see why not. Neither the C# compiler nor the runtime care if you apply those operators to non-flags enums today.
Right, C# doesn't have this restriction. I was thinking of C++, its scope enum doesn't allow those operators.
I went with enum and delegate primarily because it is relatively rare for a developer to refer to the class names System.Enum and System.Delegate directly. The use of the keywords in such a fashion also doesn't represent valid code today which could lead to any breaking changes. I do intend for the two keywords to represent the types as aliases and I personally wouldn't care if the decision was made to use System.Enum and System.Delegate instead of or in addition to the keywords.
The reason I suggested using Delegate instead of delegate is that it doesn't appear to be anything special about having a Delegate constraint, it's a constraint like any other base class constraint. Contrast this with an 'enum' constraint which is actually valuetype special constraint + System.Enum base class constraint. The overall idea is that currently keyword based constraints are special and there doesn't seem to be anything special about delegates.

Using the delegate keyword would make more sense in a hypothetical constraint that allows you to actually call the delegate, something like "where T : void delegate(int, string)".
I'm a little surprised that the first example doesn't require a conv.i2 but I guess the stack would only really contain int32 or int64 anyway.
Yes, conv.i2 actually pushes a int32 on the stack. Earlier I mentioned that it zeroes out the upper 16 bit but that's obviously wrong, conv.u2 does that. conv.i2 signs extend the lower 16 bits to 32 bits. This requires careful analysis to figure out if it's possible to omit the conversion(s) without producing bogus results. Normally the C# compiler takes care of emitting the necessary conversions but it can't do it in this case because it doesn't know the underlying type of the enum.
Nov 19, 2014 at 3:15 PM
mdanes wrote:
Halo_Four wrote:
So the CLR supports two versions of enum constraints.
Actually there are 3 versions but the 3rd isn't very useful - if you combine the class special constraint with the System.Enum base class constraint you basically say that the generic parameter can only be System.Enum and not an actual enum.
That third version sounds useless. As for System.Enum by itself, can you think of use-cases where you'd want a generic type parameter to be System.Enum and not an actual enum type? I can't.
I went with enum and delegate primarily because it is relatively rare for a developer to refer to the class names System.Enum and System.Delegate directly. The use of the keywords in such a fashion also doesn't represent valid code today which could lead to any breaking changes. I do intend for the two keywords to represent the types as aliases and I personally wouldn't care if the decision was made to use System.Enum and System.Delegate instead of or in addition to the keywords.
The reason I suggested using Delegate instead of delegate is that it doesn't appear to be anything special about having a Delegate constraint, it's a constraint like any other base class constraint. Contrast this with an 'enum' constraint which is actually valuetype special constraint + System.Enum base class constraint. The overall idea is that currently keyword based constraints are special and there doesn't seem to be anything special about delegates.

Using the delegate keyword would make more sense in a hypothetical constraint that allows you to actually call the delegate, something like "where T : void delegate(int, string)".
True. It doesn't matter much to me as long as the functionality is there. Even if eventually it did expand to supporting signatures the delegate keyword I think would still be valid in it's raw state.

mdanes wrote:
Halo_Four wrote:
It is implied that the generic type implements the common interfaces for an enum, namely IComparable, IFormattable and IConvertible.
Since this is implied it shouldn't be mentioned at all. Otherwise people may jump in an say, hey, why don't you simply use those interfaces as constraint? If the constraint is enum you obviously get an enum value which implements whatever interfaces the System.Enum base class implements.
I've reworded this so that the base type of the generic type parameter is System.Enum and no interfaces are mentioned. System.Enum implements those three interfaces publically and the net result is that all of the interface members are available but it's not correct to state that the generic type parameter directly implements those interfaces.
Nov 19, 2014 at 3:49 PM
Halo_Four wrote:
As for System.Enum by itself, can you think of use-cases where you'd want a generic type parameter to be System.Enum and not an actual enum type?
Nope. I don't think System.Enum is useful for a number of reasons:
  • It implies boxing.
  • It prevents the use of enum specific operators - & | and ^ require that the enum be in its unboxed form but the compiler can't unbox it because it doesn't know the underlying type.
  • It partially overlaps with the idea of generic - you write a generic method with a enum constraint because you want it to deal with different enum types. In that case you may as well write a non-generic method with parameters of type Enum.
Anyway, what you're proposing now is the 'enum' constraint encoded as 'valuetype System.Enum'. Implementing this doesn't prevent one from later allowing the System.Enum constraint if a good reason for that is found.
Nov 19, 2014 at 4:18 PM
lwischik wrote:
Q1. What is the type of T? Is it simply that T is some type that inherits from System.Enum but is not itself System.Enum? Is "T" known to be a value type? (hence allowing you to write T? for a nullable form of it?) What is the effective base class ($10.1.5)? Presumably it's guaranteed to satisfy "typeof(T).IsEnum"?
If System.Enum is combined with a struct constraint, the type would have to be an actual enum type; otherwise the abstract System.Enum would also be valid. In many of the usage cases I can see for having a System.Enum constraint, a method with the constraint could include code to operate just as well on System.Enum as well as on any other enumerated type (which might be useful if it is invoked from code which used System.Enum as a parameter type because of the long-standing ban on Enum constraints). For example, consider:
T LowestFlagIfAny<T>(this T it) where T:System.Enum
If invoked on an enumerated type, it would return that enumerated type, but an implementation could be written so that calling it with a parameter type of System.Enum which identified a boxed instance of System.Globalization.CompareOptions would return an instance of System.Globalization.CompareOptions boxed in a reference of type System.Enum. Alternatively, if the implementer didn't want to support System.Enum, the method could simply add a struct constraint. Consequently, I see no reason for the language to take a position on the issue.

As for CLR support, I don't think the CLR will recognize an Enum constraint as implying that a type may actually be used as an Enum. On the other hand, it is possible, without explicit CIL generation, to write a generic method of Enum.HasFlag which outperforms the generic one by about two orders of magnitude, and whose only problem is that if implemented as an extension method it will pop up on many kinds of types where it would make no sense. I would expect that a version which used Reflection.Emit might be able to achieve an additional 30-50% speedup beyond that but I don't have the expertise to do that myself(*) My best effort at a generic HasAnyFlag yielded performance which was about an order of magnitude slower than in-line code using bitwise-AND, but two orders of magnitude faster than Enum.HasFlag. Adding one order of magnitude to the time will mean that the generic HasAnyFlag shouldn't be used in performance-critical code, but code which spends 0.1% of its time on bit testing could use it with only a 1% overall time penalty. By contrast, the extra two orders of magnitude penalty for Enum.HasFlag mean that code which would spends 0.1% of its time on bit testing would have closer to a 100% time penalty.

(*) My code has a generic static class hold a Func<T,T,bool> which will be assigned to a method which accepts an integer of the delegate's underlying type. Since interface dispatch is faster than delegate dispatch, I think it would be faster to have an interface IPredicate<T>, and use Reflection.Emit to generate for each T [the first time that T is used] a singleton class which implements that interface for type T.
I guess that now you've given the syntax, the next step is to give the "static semantics" (i.e. type-checking rules).
I would suggest that the static semantics should be exactly as if the compiler had no idea that the type System.Enum used in a constraint was any different from any other class.
Q2. If x is an expression of type T, what do the member lookup rules say about members of x? as in "x.a" ? ($7.4)
The same as would be true of any other class. I would expect that if System.Enum constraint is combined with IConvertible or IFormattable, lookup would include the members of those interfaces as well as System.Enum itself; otherwise it would only include the members of System.Enum.
Q3. How is this thing exported into IL?
Exactly as if the compiler had no clue that there was anything special about System.Enum.
Q5. Presumably your enum proposal satisfies point 4 of http://www.gamasutra.com/blogs/PedroGuida/20140113/208518/Regarding_the_future_of_C_.php
The author of that blog would probably like to have an enum constraint permit the code which uses the constraint to directly treat things of the generic type as though they were enum values, but I don't think that's possible in CIL without adding a layer of Reflection which I think would be better handled in user code.
Q6. Is there any use-case other than "HasFlag" which doesn't involve reflection?
Even an efficient HasFlag implementation will need to use reflection once for each type T. As I see it, the purpose of the constraint isn't to make life nice for the implementer of the methods which include it, but rather to make it possible to write utility methods entirely in C# that would apply such constraints. Such methods could include HasAny, HasAll, LowestFlag, HighestFlag, AsString [or variations thereof, which could be much faster than ToString() and also allow more formatting options], AsInt32() [wouldn't be perfectly type-safe, but it would be just as safe as IConvertible while being boatloads faster], etc.
Q7. Are there any complications from the fact that the numeric literal 0 has an implicit conversion to enum? I don't see any.
If a method which takes two parameters of type T were passed a literal zero and an enumerated type, I think the zero would be cast to that type; given a zero and a System.Enum, I think the compiler would squawk, which would be perfectly reasonable.
Nov 19, 2014 at 4:22 PM
Here is a fleshed out example of the delegate constraint. I wrote this using C# 5.0 and used ildasm/ilasm to modify it to support the delegate constraint. This example compiles and verifies without issue. The resultant library can be consumed by C# 5.0 without additional consideration and Visual Studio correctly shows the constraint.

C# code:
public static class DelegateHelper
{
    public static void AddHandler<T>(ref T field, T handler) where T : delegate
    {
        T previous = field;
        T current;
        do
        {
            current = previous;
            T proposed = (T)Delegate.Combine(field, handler);
            previous = Interlocked.CompareExchange(ref field, proposed, current);
        } while (previous != current);
    }
}
Resultant IL:
.method public hidebysig static void  AddHandler<class ([mscorlib]System.Delegate) T>(!!T& 'field', !!T 'handler') cil managed
{
  .maxstack  3
  .locals init ([0] !!T previous,
             [1] !!T current,
             [2] !!T proposed,
             [3] bool flag)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldobj      !!T
  IL_0007:  stloc.0
  IL_0008:  nop
  IL_0009:  ldloc.0
  IL_000a:  stloc.1
  IL_000b:  ldarg.0
  IL_000c:  ldobj      !!T
  IL_0011:  box        !!T
  IL_0016:  ldarg.1
  IL_0017:  box        !!T
  IL_001c:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)
  IL_0021:  unbox.any  !!T
  IL_0026:  stloc.2
  IL_0027:  ldarg.0
  IL_0028:  ldloc.2
  IL_0029:  ldloc.1
  IL_002a:  call       !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<!!0>(!!0&, !!0, !!0)
  IL_002f:  stloc.0
  IL_0030:  nop
  IL_0031:  ldloc.0
  IL_0032:  box        !!T
  IL_0037:  ldloc.1
  IL_0038:  box        !!T
  IL_003d:  ceq
  IL_003f:  ldc.i4.0
  IL_0040:  ceq
  IL_0042:  stloc.3
  IL_0043:  ldloc.3
  IL_0044:  brtrue.s   IL_0008

  IL_0046:  ret
}
This can be consumed safely from C# 5.0 with the following code:

public class MyClass {
    private EventHandler e;

    public event EventHandler MyEvent {
        add { DelegateHelper.AddHandler(ref e, value); }
        remove { ... }
    }
}
Nov 19, 2014 at 4:36 PM
Edited Nov 19, 2014 at 4:38 PM
supercat wrote:
If System.Enum is combined with a struct constraint, the type would have to be an actual enum type; otherwise the abstract System.Enum would also be valid. In many of the usage cases I can see for having a System.Enum constraint, a method with the constraint could include code to operate just as well on System.Enum as well as on any other enumerated type (which might be useful if it is invoked from code which used System.Enum as a parameter type because of the long-standing ban on Enum constraints).
Can you provide some concrete use cases for having System.Enum be valid as the generic type parameter? I don't see an issue in supporting both, personally. Off of the top of my I can't think of such use-cases since it forces the generic type to deal with the generic type parameter as if it were only System.Enum which doesn't seem terribly useful to me.

Since I would like to retain the ability to use equality and logical operators with instances of the generic type I would like to formalize the concept of combining the System.Enum and struct constraints. I still like the use of the enum keyword to indicate that. Then the compiler can make assumptions as to what code is permitted since typeof(T).IsEnum can be guaranteed to be true.
Nov 19, 2014 at 4:43 PM
mdanes wrote:
  • It prevents the use of enum specific operators - & | and ^ require that the enum be in its unboxed form but the compiler can't unbox it because it doesn't know the underlying type.
I wouldn't expect operators like & and | to be usable on a generic constrained to System.Enum, since I don't know of any mechanism by which the compiler could achieve that other than by having one method in source code:
bool HasAnyFlags<T>(this T it, T other) where T:enum
{
  return (it & other) != 0;
}
generate four methods in the object code (taking types Int32, Int64, UInt32, and UInt64) along with the support code necessary to, for any T of an enumerated type, construct a Func<T,T,bool> which binds to the appropriate method. The amount of support code would be far less than is required for e.g. yield return, but it would be much easier to publish model boilerplate for programmers to do that in user code than to have the compiler try to auto-generate it, especially since many use cases for the constraints would end up requiring Reflection anyway (though typically once per type).
  • It partially overlaps with the idea of generic - you write a generic method with a enum constraint because you want it to deal with different enum types. In that case you may as well write a non-generic method with parameters of type Enum.
In some cases, it may be useful to have a method accept an enum-constrained generic and return another of the same type (e.g. LowestFlagIfAny). In many other cases, a parameter of type System.Enum would work semantically, but would have a huge speed penalty. A well-written method that accepts a generic enum would reduce that speed penalty enormously [by two orders of magnitude compared with Enum.HasFlag, and probably by an order of magnitude over the best possible HasFlag implementation that accepted an argument of type System.Enum (and that implementation would end up being a lot more complicated than the generic one)] Further, any method which accepts System.Enum will have to Reflection on every invocation; by contrast, a method which accepts a generic-constrained enum will have to use Reflection only once for each type T.
Nov 19, 2014 at 5:01 PM
supercat wrote:
I wouldn't expect operators like & and | to be usable on a generic constrained to System.Enum, since I don't know of any mechanism by which the compiler could achieve that other than by having one method in source code:
bool HasAnyFlags<T>(this T it, T other) where T:enum
{
  return (it & other) != 0;
}
generate four methods in the object code (taking types Int32, Int64, UInt32, and UInt64) along with the support code necessary to, for any T of an enumerated type, construct a Func<T,T,bool> which binds to the appropriate method. The amount of support code would be far less than is required for e.g. yield return, but it would be much easier to publish model boilerplate for programmers to do that in user code than to have the compiler try to auto-generate it, especially since many use cases for the constraints would end up requiring Reflection anyway (though typically once per type).
Generating additional methods would not be necessary as the IL emitted does work. The assembly verifier would need to be updated to allow the use of these operators on instances of enum-constrained generic types. I also believe that the performance of said comparisons would be just as fast as if the compiler did emit methods specific to the possibly underlying types as that should effectively be what the JIT would be doing.
Nov 19, 2014 at 5:03 PM
Halo_Four wrote:
If delegate is specified as the primary constraint then no other constraints may be specified. Instances of the generic type may be treated as if they are the System.Delegate type. They cannot be directly created using = or += except using another instance of the same type. They also cannot be directly invoked except through the DynamicInvoke method common to the System.Delegate type.
public static void Combine<T>(ref T source, T target) where T : delegate {
    Delegate combined = Delegate.Combine(source, target);
    source = (T)combined;
} 
Doesn't this mean that the following options should also work?
public static void Combine<T>(ref T source, T target) where T : delegate {
    T combined = source + target;
    source = combined;
} 

public static void Combine<T>(ref T source, T target) where T : delegate {
    source += target;
} 
If that is the case, I think you should include this or similar code in your proposal, instead of the verbose code that you almost can write today (only with a dynamic manual type check instead of an automatic static one).

Also, I assume that - will also work? That should be probably mentioned too.
Nov 19, 2014 at 5:08 PM
Halo_Four wrote:
Can you provide some concrete use cases for having System.Enum be valid as the generic type parameter? I don't see an issue in supporting both, personally. Off of the top of my I can't think of such use-cases since it forces the generic type to deal with the generic type parameter as if it were only System.Enum which doesn't seem terribly useful to me.
The primary way in which the Enum constraint is more useful than struct,IConvertible,IFormattable is that an extension method with the latter constraint will be offered as a choice on every numeric expression, but one with an Enum constraint wouldn't. Otherwise, from the perspective of the called method, all uses I can see for enum-constrained variables would require a pattern similar to:
private static class MooHelper<T>
{
  public static Func<T,T,bool> mooProc = defaultMoo;
  static bool defaltMoo(T p1, T p2) { ... set defaultProc via Reflection and call it ... }
  static bool moo(Int32 p1, Int32 p2) { ... do computation ... }
  static bool moo(Int64 p1, Int64 p2) { ... do computation ... }
  static bool moo(UInt32 p1, UInt32 p2) { ... do computation ... }
  static bool moo(UInt64 p1, UInt64 p2) { ... do computation ... }
  static bool moo(System.Enum p1, System.Enum p2) { ... do computation ... } // Optional
}
bool Moo<T>(this T it, T other) where T:System.Enum // Add a struct constraint if optional method above is omitted
{
  return MooHelper<T>.proc(it, other);
}
or else something similar using interfaces and Reflection.Emit (which might be a little faster, but would be beyond my expertise). The point is that all the ugliness is confined to a rather small piece of code, and implementation of Moo<T> would require Reflection, the call sites of Moo<T>, which are apt to be far more numerous, wouldn't. Note that from a performance standpoint, the above pattern performs an order of magnitude better than anything which could be done with a parameter of type System.Enum, since the latter scenario would either require the use of Reflection [either directly, or by calling methods of System.Enum which use it] on every invocation, or would require code to construct a ConcurrentDictionary<Type,Func<Enum,Enum,bool>> so that when given an enum which is backed by an Int16, it would invoke a method
bool HandleEnumInt16(Enum p1, Enum p2, bool)
{
  var pp1=(Int16)p1,pp2=(Int16)p2;
  ... code now use operators like & and | on pp1 and pp2
}
Dispatch using a dictionary on GetType() would probably be faster than using reflection any other way, but would still be much slower than the much-more-direct dispatch available using a static generic type.
Since I would like to retain the ability to use equality and logical operators with intances of the generic type I would like to formalize the concept of combining the System.Enum and struct constraints. I still like the use of the enum keyword to indicate that. Then the compiler can make assumptions as to what code is permitted since typeof(T).IsEnum can be guaranteed to be true.
I don't think you can regain the ability to use equality and logical operators with instances of the generic type unless the compiler implements all the above boilerplate.
Nov 19, 2014 at 5:16 PM
supercat wrote:
Halo_Four wrote:
Since I would like to retain the ability to use equality and logical operators with intances of the generic type I would like to formalize the concept of combining the System.Enum and struct constraints. I still like the use of the enum keyword to indicate that. Then the compiler can make assumptions as to what code is permitted since typeof(T).IsEnum can be guaranteed to be true.
I don't think you can regain the ability to use equality and logical operators with instances of the generic type unless the compiler implements all the above boilerplate.
mdanes and I have both already tested this and it works fine. The only problem currently is that the verifier doesn't expect it and complains but I think that the verifier could be modified quite easily to support those operations if the generic type parameter has the valuetype and System.Enum constraints.
Nov 19, 2014 at 5:18 PM
svick wrote:
Halo_Four wrote:
If delegate is specified as the primary constraint then no other constraints may be specified. Instances of the generic type may be treated as if they are the System.Delegate type. They cannot be directly created using = or += except using another instance of the same type. They also cannot be directly invoked except through the DynamicInvoke method common to the System.Delegate type.
public static void Combine<T>(ref T source, T target) where T : delegate {
    Delegate combined = Delegate.Combine(source, target);
    source = (T)combined;
} 
Doesn't this mean that the following options should also work?
public static void Combine<T>(ref T source, T target) where T : delegate {
    T combined = source + target;
    source = combined;
} 

public static void Combine<T>(ref T source, T target) where T : delegate {
    source += target;
} 
If that is the case, I think you should include this or similar code in your proposal, instead of the verbose code that you almost can write today (only with a dynamic manual type check instead of an automatic static one).

Also, I assume that - will also work? That should be probably mentioned too.
You're right and I can update the proposal to reflect this. I'd probably use a different example, however, as using += or -= within the above method is not thread-safe and I don't want to promote that pattern, even if these samples are just illustrative.
Nov 19, 2014 at 5:33 PM
Edited Nov 19, 2014 at 7:30 PM
Here is another example using an enum generic type constraint. Again, authored in C# and used ilasm/ildasm to manually fix up the IL and apply the constraint. As with the delegate example this code works fine and can be consumed from C# 5.0 without any issues. However, the assembly does not verify as unboxed T is unexpected.
public static class EnumHelper {
    public static bool HasFlag<T>(this T it, T flag) where T : enum {
        return (it & flag) != default(T);
    }
}
The generated IL:
.method public hidebysig static bool  HasFlag<valuetype .ctor ([mscorlib]System.Enum) T>(!!T it, !!T flag) cil managed
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  .maxstack  2
  .locals init ([0] !!T def,
             [1] bool flag)
  IL_0000:  nop
  IL_0001:  ldloca.s   def
  IL_0003:  initobj    !!T
  IL_0009:  ldarg.0
  IL_000a:  ldarg.1
  IL_000b:  and
  IL_000c:  ldloc.0
  IL_000d:  ceq
  IL_000f:  ldc.i4.0
  IL_0010:  ceq
  IL_0012:  stloc.1
  IL_0013:  br.s       IL_0015

  IL_0015:  ldloc.1
  IL_0016:  ret
} // end of method EnumHelper::HasFlag
This code can be consumed as follows:
MyEnum e1 = MyEnum.Foo;
bool has = e1.HasFlag<MyEnum>(MyEnum.Bar); // explicitly using the generic type parameter to disambiguate from Enum.HasFlag
Also, I ran this through a simple benchmark against Enum.HasFlag and this extension method comes out about 8x faster than the framework-provided method.
Nov 19, 2014 at 5:36 PM
Halo_Four wrote:
You're right and I can update the proposal to reflect this. I'd probably use a different example, however, as using += or -= within the above method is not thread-safe and I don't want to promote that pattern, even if these samples are just illustrative.
It's worth noting that the current C# specification doesn't allow + and - to be used on System.Delegate itself:

7.8.4 ... Since System.Delegate is not a delegate type, operator + is not defined for it.

Operator + simply calls Delegate.Combine so I suppose adding support for this shouldn't be a problem.

But this brings up an interesting question - do you really want that constraint to be System.Delegate? If you want to use Delegate.Combine it would make more sense to use System.MulticastDelegate as constraint. Maybe there's more to this constraint than I suspected...
Nov 19, 2014 at 5:46 PM
Halo_Four wrote:
Here is another example using an enum generic type constraint. ...
0 should be replaced by default(T), otherwise this won't work because that 0 needs to have a particular type - i4, i8 etc.
.method public hidebysig static bool HasFlag<valuetype .ctor ([mscorlib]System.ValueType) T>(!!T it, !!T flag) cil managed
I think you wanted to say System.Enum, not System.ValueType.
Also, I ran this through a simple benchmark against Enum.HasFlag and this extension method comes out about 8x faster than the framework-provided method.
Of course, the generated code is identical to the code that you get when you manually test for flags.
Nov 19, 2014 at 5:46 PM
mdanes wrote:
Halo_Four wrote:
You're right and I can update the proposal to reflect this. I'd probably use a different example, however, as using += or -= within the above method is not thread-safe and I don't want to promote that pattern, even if these samples are just illustrative.
It's worth noting that the current C# specification doesn't allow + and - to be used on System.Delegate itself:

7.8.4 ... Since System.Delegate is not a delegate type, operator + is not defined for it.

Operator + simply calls Delegate.Combine so I suppose adding support for this shouldn't be a problem.

But this brings up an interesting question - do you really want that constraint to be System.Delegate? If you want to use Delegate.Combine it would make more sense to use System.MulticastDelegate as constraint. Maybe there's more to this constraint than I suspected...
That's interesting given that concrete delegates and System.MulticastDelegate don't implement + operators either. The language always converts that to a call to Delegate.Combine. Perhaps the language restriction exists to attempt to enforce type-safety at compile-time. I can see how that would be an issue if the caller specified System.Delegate as the generic type parameter, but that would also be the case if the generic type parameter was System.MulticastDelegate since all delegates technically derive from that type.
Nov 19, 2014 at 5:55 PM
mdanes wrote:
Halo_Four wrote:
Here is another example using an enum generic type constraint. ...
0 should be replaced by default(T), otherwise this won't work because that 0 needs to have a particular type - i4, i8 etc.
Changed. It does work fine with 0, even if the enum is of type long, I assume because the run time will automatically expand the i4 to an i8 if necessary.
.method public hidebysig static bool HasFlag<valuetype .ctor ([mscorlib]System.ValueType) T>(!!T it, !!T flag) cil managed
I think you wanted to say System.Enum, not System.ValueType.
Oops, good catch. I must've accidentally re-disassembled the reference project before copying the IL. Updated and confirmed that it still works.
Also, I ran this through a simple benchmark against Enum.HasFlag and this extension method comes out about 8x faster than the framework-provided method.
Of course, the generated code is identical to the code that you get when you manually test for flags.
Which is exactly what I would expect.
Nov 19, 2014 at 6:03 PM
Halo_Four wrote:
That's interesting given that concrete delegates and System.MulticastDelegate don't implement + operators either. The language always converts that to a call to Delegate.Combine. Perhaps the language restriction exists to attempt to enforce type-safety at compile-time. I can see how that would be an issue if the caller specified System.Delegate as the generic type parameter, but that would also be the case if the generic type parameter was System.MulticastDelegate since all delegates technically derive from that type.
Yes, if you add a + operator to Delegate/MulticastDelegate then you can add delegates of different types and that fails at runtime. This can happen in generic methods too:
T foo<T>(T x, U y) where T : delegate, where U : delegate { return x + y; } // this can fail, T and U may be different delegate types
But your typical example should work:
T foo<T>(T x, T y) where T : delegate { return x + y; } // OK, it's the same T and delegates are sealed
But there's a catch - this means that the delegate constraint should reject the System.Delegate type itself, much like the enum constraint rejects System.Enum. If you allow T to be System.Delegate then x may be Action and y may be Func and attempting to combine them will result in an exception.
Nov 19, 2014 at 6:28 PM
mdanes wrote:
Halo_Four wrote:
That's interesting given that concrete delegates and System.MulticastDelegate don't implement + operators either. The language always converts that to a call to Delegate.Combine. Perhaps the language restriction exists to attempt to enforce type-safety at compile-time. I can see how that would be an issue if the caller specified System.Delegate as the generic type parameter, but that would also be the case if the generic type parameter was System.MulticastDelegate since all delegates technically derive from that type.
Yes, if you add a + operator to Delegate/MulticastDelegate then you can add delegates of different types and that fails at runtime. This can happen in generic methods too:
T foo<T>(T x, U y) where T : delegate, where U : delegate { return x + y; } // this can fail, T and U may be different delegate types
But your typical example should work:
T foo<T>(T x, T y) where T : delegate { return x + y; } // OK, it's the same T and delegates are sealed
But there's a catch - this means that the delegate constraint should reject the System.Delegate type itself, much like the enum constraint rejects System.Enum. If you allow T to be System.Delegate then x may be Action and y may be Func and attempting to combine them will result in an exception.
The problem with that being that the CLR constraint I don't think can enforce such a rule. I'd be willing to forego the operator-like syntax for Delegate.Combine and Delegate.Remove if those problems can't be sufficiently solved. That would force the user to fallback to using the actual methods which are just as potentially error-prone but don't offer the illusion of type-safety.
Nov 19, 2014 at 6:41 PM
Edited Nov 19, 2014 at 6:52 PM
mdanes wrote:
But your typical example should work:
T foo<T>(T x, T y) where T : delegate { return x + y; } // OK, it's the same T and delegates are sealed
Consider:
    interface i1 { }
    interface i2 { }
    interface i12a : i1, i2 { }
    interface i12b : i1, i2 { }
    static void blah1(i1 it) { Console.Write("I1"); }
    static void blah2(i2 it) { Console.Write("I2"); }
    static void dtest()
    {
        Action<i1> a1 = blah1;
        Action<i2> a2 = blah2;
        Action<i12a> a1a = a1;
        Action<i12a> a2a = a2;
        Action<i12b> a1b = a1;
        Action<i12b> a2b = a2;
        Action<i12a> a12a = foo(a1a, a2a);
        Action<i12b> a12b = foo(a1b, a2b);
        a12a(null);
        a12b(null);
    }

Are a1a and a2a the same type? What will be the effect of foo(act1a, act2a)? It would be possible to write a generic foo in such a way as to yield an Action<i12> which outputs "I1I2", but the + operator will throw an exception. Note that for a method invoked in that fashion to work, the necessary type for the resulting delegate must be supplied as a generic type parameter; it cannot be inferred from the passed-in references alone [an Action<i1> and an Action<i2> could be combined to yield either an Action<i12a> or an Action<i12b>; a delegate-combining method would have no reason to favor one over the other, but the first invocation must yield Action<i12a> and the second Action<i12b>].
Nov 19, 2014 at 6:56 PM
supercat wrote:
mdanes wrote:
But your typical example should work:
T foo<T>(T x, T y) where T : delegate { return x + y; } // OK, it's the same T and delegates are sealed
    interface i1 { }
    interface i2 { }
    interface i12 : i1, i2 { }
    static void blah1(i1 it) { Console.Write("I1"); }
    static void blah2(i2 it) { Console.Write("I2"); }
    static void dtest3()
    {
        Action<i1> a1a = blah1;
        Action<i12> a1b = a1a;
        Action<i2> a2a = blah2;
        Action<i12> a2b = a2a;
        Action<i12> a12b = foo(a1b,a2b);
    }
Are a1b and a2b the same type? What will be the effect of foo(act1, act2b)? It would be possible to write a generic foo in such a way as to yield an Action<i12> which outputs "I1I2", but the + operator will throw an exception. Note that for a method invoked in that fashion to work, the necessary type for the resulting delegate must be supplied as a generic type parameter; it cannot be inferred from the passed-in references alone!
So would Delegate.Combine without some manual fix-up. This is code that walks the fine line between being generic and playing loose with the types internally, which is all fine and good for very specific use-cases. I think that if a delegate constraint does permit raw System.Delegate that it would enable your scenario and in a type-safe manner (for the caller) but I think it precludes the ability to use the language helper "operators" within those generic methods.
Nov 19, 2014 at 7:00 PM
Halo_Four wrote:
The only problem currently is that the verifier doesn't expect it and complains but I think that the verifier could be modified quite easily to support those operations if the generic type parameter has the valuetype and System.Enum constraints.
How easy is it to modify the verifier that's built into existing .NET installations? From my understanding, if an assembly is marked "unsafe", then existing .NET installations will allow it to run even if it's unverified if the execution context allows running unsafe code, but a compiler shouldn't generate "unsafe" code without a good reason. The fact that a programmer uses the unsafe keyword to request the creation of pointers is a good reason. Using generic operations on an enum, not so much.
Nov 19, 2014 at 7:09 PM
Halo_Four wrote:
Also, I ran this through a simple benchmark against Enum.HasFlag and this extension method comes out about 8x faster than the framework-provided method.
How does it compare with
    private static class Helper<T>
    {
        static bool DoIt(Int32 a, Int32 b) { return (a & b) != 0; }
        static bool DoIt(Int64 a, Int64 b) { return (a & b) != 0; }
        public static Func<T, T, bool> doItProc = doItDefault;
        static bool doItDefault(T a, T b)
        {
            doItProc = (Func<T,T,bool>)Delegate.CreateDelegate(typeof(Func<T,T,bool>), typeof(Helper<T>), "DoIt");
            return doItProc(a, b);
        }
    }
    public static bool HasAny<T>(this T it, T other) where T:struct,IComparable
    {
        return Helper<T>.doItProc(it, other);
    }
[a better version should support UInt64, etc. but for benchmarking the above should be fine].
Nov 19, 2014 at 7:11 PM
supercat wrote:
Halo_Four wrote:
The only problem currently is that the verifier doesn't expect it and complains but I think that the verifier could be modified quite easily to support those operations if the generic type parameter has the valuetype and System.Enum constraints.
How easy is it to modify the verifier that's built into existing .NET installations? From my understanding, if an assembly is marked "unsafe", then existing .NET installations will allow it to run even if it's unverified if the execution context allows running unsafe code, but a compiler shouldn't generate "unsafe" code without a good reason. The fact that a programmer uses the unsafe keyword to request the creation of pointers is a good reason. Using generic operations on an enum, not so much.
Simple, issue an update. Wouldn't be the first time.

This code is perfectly safe and is guaranteed to execute as expected. PEVerify.exe just hasn't seen this case before. I think it makes infinitely more sense to make it a supported scenario and have the compiler emit simple code rather than having the compiler spit out a bunch of hidden implementation methods and have to test the type of the enum in order to dispatch correctly. Afterall, that exactly what the JIT will be doing.
Nov 19, 2014 at 7:18 PM
Halo_Four wrote:
So would Delegate.Combine without some manual fix-up. This is code that walks the fine line between being generic and playing loose with the types internally, which is all fine and good for very specific use-cases. I think that if a delegate constraint does permit raw System.Delegate that it would enable your scenario and in a type-safe manner (for the caller) but I think it precludes the ability to use the language helper "operators" within those generic methods.
Add a nice little generic extension method Fix:
    static class DelegateTransformer<T> where T:class
    {
        public static System.Runtime.CompilerServices.ConditionalWeakTable<Delegate, T> lookup =
            new System.Runtime.CompilerServices.ConditionalWeakTable<Delegate, T>();
        public static T help(Delegate d)
        {
            var arr = d.GetInvocationList();
            if (arr.Length == 1)
                return (T)(Object)Delegate.CreateDelegate(typeof(T), d.Target, d.Method);
            else
            {
                for (int i=0; i<arr.Length; i++)
                    arr[i] = (Delegate)(Object)Fix<T>((T)(Object)arr[i]);
                return (T)(Object)Delegate.Combine(arr);
            }
        }
    }
    public static T Fix<T>(this T it) where T:class
    {
        if (it == null) return it;
        if (it.GetType() == typeof(T)) return it;
        return DelegateTransformer<T>.lookup.GetValue((Delegate)(Object)it, DelegateTransformer<T>.help);
    }
and it will be possible to safely say a1a.Fix() + a2a.Fix() or a1b.Fix() + a2b.Fix() and receive a delegate of suitable type. Without generics, the code would have to be a1a.Fix(typeof(I12a)) + a2a.Fix(typeof(I12a)) which seems much more verbose and--unlike the original--isn't typesafe.
Nov 19, 2014 at 7:30 PM
Edited Nov 19, 2014 at 7:37 PM
supercat wrote:
Halo_Four wrote:
Also, I ran this through a simple benchmark against Enum.HasFlag and this extension method comes out about 8x faster than the framework-provided method.
How does it compare with
    private static class Helper<T>
    {
        static bool DoIt(Int32 a, Int32 b) { return (a & b) != 0; }
        static bool DoIt(Int64 a, Int64 b) { return (a & b) != 0; }
        public static Func<T, T, bool> doItProc = doItDefault;
        static bool doItDefault(T a, T b)
        {
            doItProc = (Func<T,T,bool>)Delegate.CreateDelegate(typeof(Func<T,T,bool>), typeof(Helper<T>), "DoIt");
            return doItProc(a, b);
        }
    }
    public static bool HasAny<T>(this T it, T other) where T:struct,IComparable
    {
        return Helper<T>.doItProc(it, other);
    }
[a better version should support UInt64, etc. but for benchmarking the above should be fine].
With int.MaxValue iterations:
EnumHelper.HasFlags:  00:00:09.7028893  (Using IL posted to this thread)
EnumHelper.HasAny:    00:00:15.0950521  (Using your code, wrapped in static class EnumHelper)
Enum.HasFlag:         00:01:32.9203643  (my 8x math might be a little off)
The IL above uses initobj to specify default(T) for the enum, which is technically correct. If the compiler could instead emit ldc.i4.0 it can shave more than another full second (00:00:08.6603315) from this simple benchmark. The runtime does happily expand that constant to 64-bit where appropriate and I believe that it can be considered safe and the verifier doesn't squawk about it at all.
Nov 19, 2014 at 8:45 PM
supercat wrote:
Are a1a and a2a the same type? What will be the effect of foo(act1a, act2a)?
That won't work, Delegate.Combine only works if the delegates have the exact same type.

Since Delegate.Combine and the + operator provided by the compiler don't attempt to avoid this problem there's no reason for generic version of the + operator to do this either. Allowing the delegate constraint and allowing + to be used in such methods doesn't make things worse.
Nov 19, 2014 at 9:18 PM
Edited Nov 19, 2014 at 9:20 PM
mdanes wrote:
Since Delegate.Combine and the + operator provided by the compiler don't attempt to avoid this problem there's no reason for generic version of the + operator to do this either. Allowing the delegate constraint and allowing + to be used in such methods doesn't make things worse.
A generic extension method Plus<T> could avoid the problem by invoking upon each argument the Fix extension method supplied above. If invoked upon a value of a concrete delegate type, it will yield a delegate instance of that exact type. For example, both act1a.Fix() and act2a.Fix() would yield instances of Action<i12a>, which could be safely combined. The Fix method is more proof-of-concept than production-ready; if invoked upon a value of type Delegate or MulticastDelegate it should probably simply return the argument unaltered. Still I think that if d1 and d2 are variables of the same concrete delegate type and d1==d2 is true, then I believe d1.Fix()==d2.Fix() will also be true, which should make the method safe for use when managing events.
Nov 19, 2014 at 9:34 PM
Halo_Four wrote:
The IL above uses initobj to specify default(T) for the enum, which is technically correct. If the compiler could instead emit ldc.i4.0 it can shave more than another full second (00:00:08.6603315) from this simple benchmark. The runtime does happily expand that constant to 64-bit where appropriate and I believe that it can be considered safe and the verifier doesn't squawk about it at all.
It can't be ldc.i4.0 because if you use a enum those underlying type is i8 you end up with a ceq on a int32 and a int64, that's not valid IL.

The initobj slowness appears to be caused by a JIT problem - default(EnumX) generates fast code, default(T) where T is EnumX generates slow code.
Nov 19, 2014 at 9:40 PM
Edited Nov 19, 2014 at 9:40 PM
supercat wrote:
A generic extension method Plus<T> could avoid the problem by invoking upon each argument the Fix extension method supplied above.
It could but what has this to do with the delegate constraint - the subject of this thread? Are you suggesting that the + operator that we were talking about should apply this fix automatically?
Nov 19, 2014 at 10:57 PM
mdanes wrote:
It could but what has this to do with the delegate constraint - the subject of this thread? Are you suggesting that the + operator that we were talking about should apply this fix automatically?
I don't think the operator itself should be changed, but one of the uses for which System.Delegate would be useful as a constraint type would be to allow an extension method like Fix() [probably with some better name like AsDeclaredType()] to pop up as an option for delegates without having it pop up as a choice for all reference types.

Something like myActions.SafeCombine(otherAction); or myActions + otherAction.AsDeclaredType() may not be as concise as myActions + otherAction, but it's a lot nicer than using myActions.safeCombine(otherAction, typeof(TheTypeOfMyAction)) or having DelegateHelper.CombineActions(myActions, otherActions) use parameters of an unconstrained generic type.
Nov 19, 2014 at 11:28 PM
supercat wrote:
mdanes wrote:
It could but what has this to do with the delegate constraint - the subject of this thread? Are you suggesting that the + operator that we were talking about should apply this fix automatically?
I don't think the operator itself should be changed, but one of the uses for which System.Delegate would be useful as a constraint type would be to allow an extension method like Fix() [probably with some better name like AsDeclaredType()] to pop up as an option for delegates without having it pop up as a choice for all reference types.

Something like myActions.SafeCombine(otherAction); or myActions + otherAction.AsDeclaredType() may not be as concise as myActions + otherAction, but it's a lot nicer than using myActions.safeCombine(otherAction, typeof(TheTypeOfMyAction)) or having DelegateHelper.CombineActions(myActions, otherActions) use parameters of an unconstrained generic type.
I wonder if it is worth having the generic type constraint allow raw System.Delegate in said cases, though. You could always write a helper method which took one or more instances of System.Delegate and a generic type parameter of the concrete target delegate type and have it perform this fixing operation that you describe, e.g.:
public static T FixAndCombine<T>(params Delegate[] delegates) where T : delegate { ... }
That said, given that the C# already supports the consumption of delegate constraints and already allows the use of System.Delegate as the type constraint I don't know if it's worth entertaining the notion of modifying that behavior despite the rarity of this being used today. Combining the constraint with a secondary constraint doesn't make much sense either.
Nov 20, 2014 at 12:03 AM
Halo_Four wrote:
I wonder if it is worth having the generic type constraint allow raw System.Delegate in said cases, though. You could always write a helper method which took one or more instances of System.Delegate and a generic type parameter of the concrete target delegate type and have it perform this fixing operation that you describe, e.g.:

```cs
public static T FixAndCombine<T>(params Delegate[] delegates) where T : delegate { ... }
That would require writing a separate FixAndCombine for every different kind of delegate one wanted to use, and would also require the caller to supply the type T since there would be no way it could be inferred otherwise. Try the Fix method above and you'll notice that it simply works; the only problem is that it pops up in Intellisense in many cases where it shouldn't.
That said, given that the C# already supports the consumption of delegate constraints and already allows the use of System.Delegate as the type constraint I don't know if it's worth entertaining the notion of modifying that behavior despite the rarity of this being used today. Combining the constraint with a secondary constraint doesn't make much sense either.
When did C# start allowing such constraints?
Nov 20, 2014 at 12:23 AM
supercat wrote:
Halo_Four wrote:
I wonder if it is worth having the generic type constraint allow raw System.Delegate in said cases, though. You could always write a helper method which took one or more instances of System.Delegate and a generic type parameter of the concrete target delegate type and have it perform this fixing operation that you describe, e.g.:

```cs
public static T FixAndCombine<T>(params Delegate[] delegates) where T : delegate { ... }
That would require writing a separate FixAndCombine for every different kind of delegate one wanted to use, and would also require the caller to supply the type T since there would be no way it could be inferred otherwise. Try the Fix method above and you'll notice that it simply works; the only problem is that it pops up in Intellisense in many cases where it shouldn't.
It wouldn't require a different extension method for each delegate type. It would still require "fixing" the delegates to all be of the same type, though. However, even if delegate constraints were limited to concrete delegates and not System.Delegate I don't see why that would affect your extension method. Why would a consumer want to call it with System.Delegate? It wouldn't work.
That said, given that the C# already supports the consumption of delegate constraints and already allows the use of System.Delegate as the type constraint I don't know if it's worth entertaining the notion of modifying that behavior despite the rarity of this being used today. Combining the constraint with a secondary constraint doesn't make much sense either.
When did C# start allowing such constraints?
Crazy thing is that C# has always allowed consuming those constraints. Visual Studio can interpret them and display them correctly too.
Nov 20, 2014 at 6:30 AM
mdanes wrote:
Yes, conv.i2 actually pushes a int32 on the stack. Earlier I mentioned that it zeroes out the upper 16 bit but that's obviously wrong, conv.u2 does that. conv.i2 signs extend the lower 16 bits to 32 bits. This requires careful analysis to figure out if it's possible to omit the conversion(s) without producing bogus results. Normally the C# compiler takes care of emitting the necessary conversions but it can't do it in this case because it doesn't know the underlying type of the enum.
Turns out that dealing with conversion is quite a bit more complicated. And the simplest example where this can be seen is GetValue:

The int returning version doesn't need any conversion because on the IL stack the value is already an int. But if you want to return a long then you need a conv.u8 or a conv.i8 depending on the underlying type of the enum. The C# compiler can't figure out what conv to emit because it doesn't know the underlying type at compile time so something else needs to be done here:
  • defer the conversion to a helper method such as Convert.ToInt64 - very inefficient due to boxing and call through interface
  • emit code that figures out the underlying type at runtime (calling GetUnderlyingType) and selects the appropriate conversion
The second version would be preferable but calling GetUnderlyingType isn't exactly fast since it relies on reflection in its current implementation. GetTypeCode would be better but it too relies on GetUnderlyingType and it's not available in all framework profiles.

Ultimately the only way to implement this efficiently is to do it in such a way that the JIT compiler recognizes the type detection and treats the type as a compile time constant, that would allow it to eliminate a if/switch that would probably cost more than the actual conversion. A rather ugly complication I'm afraid...
Nov 20, 2014 at 2:11 PM
Edited Nov 20, 2014 at 2:12 PM
I'll jump in and mention that after more consideration, I'd much rather see the C# team work on improving the functionality of enums rather than removing the "no enum constraint" rule. Adding enum constraints at this point probably makes any investment in improving enums later on much more difficult.

C# enums are vastly inferior to those of Java and other languages- they can only represent integers and cannot have methods. A long-standing community request is for string enums, and I'd like to see methods/properties on enums added as well. Thus, if string enums were ever added, you'd probably at minimum need to be able to express the underlying type of the enum in any constraint.

I think delegate constraints are probably still a good idea. However, an important point to consider here is if a means of structural typing is ever added into C#, how that would affect this feature. I don't know the answer to that question as there's been no proposal on structural typing (only wishful thinking), but again if adding delegate constraints now made doing some kind of structural typing later on more difficult, I'd rather punt on it.
Nov 20, 2014 at 3:26 PM
MgSam wrote:
A long-standing community request is for string enums, and I'd like to see methods/properties on enums added as well.
Can't you just use extension methods? Extension properties were already requested separately. Since enums are stateless, you should be covered for all scenarios.

I would be more interested in string enums inheritance/mixing-in if that is possible in a simple idiomatic way. Or maybe static class inheritance. This way I could express e.g. hierarchies of possible states rather than duplicating them in a loose way.
Nov 20, 2014 at 3:39 PM
@dsaf Extension methods work, yes, but are cumbersome to write as they need to be in a whole separately scoped static class. They also are just compiler-magic and as such aren't associated with their type at runtime. That being said, in truth, I think the String enums are a much bigger pain point.
Nov 20, 2014 at 4:27 PM
mdanes wrote:
Ultimately the only way to implement this efficiently is to do it in such a way that the JIT compiler recognizes the type detection and treats the type as a compile time constant, that would allow it to eliminate a if/switch that would probably cost more than the actual conversion. A rather ugly complication I'm afraid...
Using a generic static class to hold a delegate that needs to be constructed only once for each type T isn't ideal from a performance standpoint, but doesn't require any boxing and ends up performing vastly faster than a non-generic method.
Nov 20, 2014 at 5:06 PM
MgSam wrote:
C# enums are vastly inferior to those of Java and other languages- they can only represent integers and cannot have methods. A long-standing community request is for string enums, and I'd like to see methods/properties on enums added as well. Thus, if string enums were ever added, you'd probably at minimum need to be able to express the underlying type of the enum in any constraint.
I wish the type system had been designed in such a way as to allow a more flexible kind of hierarchy for value types. An aspect of .NET which is simultaneously clever but limiting is that a single type definition for any type derived from System.ValueType, with two specific hard-coded exceptions System.Enum and System.ValueType, simultaneously defines a storage-location type and a boxed heap object type. Having strongly-associated storage-location types and heap object types is a good thing, but the design forces the ValueType hierarchy to be flat, and precludes the possibility of defining any new kinds of value types for different purposes, since the statements "A thing of type X is a thing of type Y" and "A thing of type X can be stored into a storage location of type Y" imply different relationships, which would imply that a hierarchy of storage location types won't fit the same mold as a hierarchy of object types.

I don't know what obstacles there would be to allowing derivatives of System.Enum to include methods and implement interfaces, but I don't see that allowing System.Enum as a constraint would impede that. At worst, it might encourage people to use a programming approach which might not be as efficient as an approach that might become available in the future, but since people would have the benefits of being able to do something useful in the years before the new approach becomes available, I wouldn't see that as a huge loss.
I think delegate constraints are probably still a good idea. However, an important point to consider here is if a means of structural typing is ever added into C#, how that would affect this feature. I don't know the answer to that question as there's been no proposal on structural typing (only wishful thinking), but again if adding delegate constraints now made doing some kind of structural typing later on more difficult, I'd rather punt on it.
As above, I would say that if structural typing becomes available (I would like to have the Runtime auto-generate a set of interfaces associated with delegates and have delegates auto-implement them, so that e.g. a delegate int Fnorble(int moe, ref string larry, object curly) would implement something like System.DelegateInterfaces.IFuncVRV<int, string, object, int> [or, if not for the convention of including the return type of a function in its parameter list, System.DelegateInterfaces.IFuncVRV<int, string, object>.Returning<int>]. Having the CLR provide such auto-implementations would I think eliminate the need to have the Runtime or languages do anything else to provide structural typing of delegates.
Nov 20, 2014 at 5:13 PM
dsaf wrote:
Can't you just use extension methods? Extension properties were already requested separately. Since enums are stateless, you should be covered for all scenarios.
If enumerated types could implement interfaces, that would make possible many things which can't be handled very well otherwise. For example, there are many situations where a number of enumerated types might use some bits for a common purpose, and other bits for different purposes. If enumerated types could be declared as implementing interfaces--even if they couldn't actually include code for any methods--then it would be possible for a method to accept a parameter constrained as struct,IOperatingModeBits4to6. If enumerated types could implement methods/properties, that would be even better since an interface property Int32 OperatingMode { get {return (value >> 4) & 7;}} could probably be expanded as in-line code, but even if the enumerated types couldn't implement methods the situation would still be better than it is now.
Nov 20, 2014 at 5:45 PM
MgSam wrote:
C# enums are vastly inferior to those of Java and other languages- they can only represent integers and cannot have methods. A long-standing community request is for string enums, and I'd like to see methods/properties on enums added as well. Thus, if string enums were ever added, you'd probably at minimum need to be able to express the underlying type of the enum in any constraint.
C# enums and Java enums are very different concepts, though. Java enums are just syntax candy built on defining normal classes where the static constructor/initializer constructs a singleton for each specified value. That's perfectly doable with C# today using the more verbose syntax, although you'd still have to rely on instance equality comparisons using if/then statements.

I can't see C# ever getting Java-style enums, at least as anything called enums, because that would be very confusing with the existing enum syntax. However, with the proposals for record types I wouldn't be surprised if the idea is revisited in C# 7.0.
Nov 20, 2014 at 5:47 PM
MgSam wrote:
I'll jump in and mention that after more consideration, I'd much rather see the C# team work on improving the functionality of enums rather than removing the "no enum constraint" rule. Adding enum constraints at this point probably makes any investment in improving enums later on much more difficult.
Also, I don't think that the two issues are intertwined. If you remove some of the nice syntactical fluff I added to this proposal just to have something to argue through there is exceptionally little work that the C# team would have to do in order to support having enum constraints. Honestly, if they just commented out a few existing pieces of code it would already work. Everything already works in the CLR proper and C# already supports consuming those constraints from assemblies defined in the few languages that already support them (IL and Oxygene to name two).
Coordinator
Nov 20, 2014 at 5:51 PM
Halo_Four wrote:
Honestly, if they just commented out a few existing pieces of code it would already work.
I earnestly hope this attitude goes away. Something would just work, sure. But as seen from this thread, that's a long way of knowing if it's the right thing.
Nov 20, 2014 at 5:56 PM
mdanes wrote:
Halo_Four wrote:
The IL above uses initobj to specify default(T) for the enum, which is technically correct. If the compiler could instead emit ldc.i4.0 it can shave more than another full second (00:00:08.6603315) from this simple benchmark. The runtime does happily expand that constant to 64-bit where appropriate and I believe that it can be considered safe and the verifier doesn't squawk about it at all.
It can't be ldc.i4.0 because if you use a enum those underlying type is i8 you end up with a ceq on a int32 and a int64, that's not valid IL.
Hrm. You're right that int32 and int64 are not a valid pair of types for comparison according to the IL spec. The verifier doesn't complain about it, though, and it appears to run fine even when the enum is ulong.

Even so, the initobj version gets a quite respectable result.
Nov 20, 2014 at 6:00 PM
lwischik wrote:
Halo_Four wrote:
Honestly, if they just commented out a few existing pieces of code it would already work.
I earnestly hope this attitude goes away. Something would just work, sure. But as seen from this thread, that's a long way of knowing if it's the right thing.
It's hard to not have that attitude, though, given that the CLR supports these constraints as do other commercial CLR languages, especially without understanding the reasons that the C# team decided to impose the limitation. If I recall correctly supercat already did attempt to comment out the compiler errors and it did function as expected.

I do agree that it's not productive to just throw that around but I intended my comment to read more like, "why not spend some effort on these potentially smaller items instead of pouring all effort onto the large unknowns."
Nov 20, 2014 at 6:02 PM
mdanes wrote:
mdanes wrote:
Yes, conv.i2 actually pushes a int32 on the stack. Earlier I mentioned that it zeroes out the upper 16 bit but that's obviously wrong, conv.u2 does that. conv.i2 signs extend the lower 16 bits to 32 bits. This requires careful analysis to figure out if it's possible to omit the conversion(s) without producing bogus results. Normally the C# compiler takes care of emitting the necessary conversions but it can't do it in this case because it doesn't know the underlying type of the enum.
Turns out that dealing with conversion is quite a bit more complicated. And the simplest example where this can be seen is GetValue:

The int returning version doesn't need any conversion because on the IL stack the value is already an int. But if you want to return a long then you need a conv.u8 or a conv.i8 depending on the underlying type of the enum. The C# compiler can't figure out what conv to emit because it doesn't know the underlying type at compile time so something else needs to be done here:
  • defer the conversion to a helper method such as Convert.ToInt64 - very inefficient due to boxing and call through interface
  • emit code that figures out the underlying type at runtime (calling GetUnderlyingType) and selects the appropriate conversion
The second version would be preferable but calling GetUnderlyingType isn't exactly fast since it relies on reflection in its current implementation. GetTypeCode would be better but it too relies on GetUnderlyingType and it's not available in all framework profiles.

Ultimately the only way to implement this efficiently is to do it in such a way that the JIT compiler recognizes the type detection and treats the type as a compile time constant, that would allow it to eliminate a if/switch that would probably cost more than the actual conversion. A rather ugly complication I'm afraid...
Actually I think you are taking this further than I had intended. The example code, as written, should always emit the IL conv.i4 as the developer specified a cast to an int. If the underlying type of the enum was an int then the conversion would be effectively a no-op and if the underlying type was a long the value would be truncated. This is consistent with the behavior today if the programmer casts from an arbitrary enum to an int, or any of the other integral types.
Coordinator
Nov 20, 2014 at 6:06 PM
Halo_Four wrote:
It's hard to not have that attitude, though, given that the CLR supports these constraints as do other commercial CLR languages, especially without understanding the reasons that the C# team decided to impose the limitation. If I recall correctly supercat already did attempt to comment out the compiler errors and it did function as expected.
The danger is that if we unblock it now with the syntax "where T:enum", and it proves not quite to hit the spot, then we'll be forever unable to use that syntax for a more useful enum constraint. Moreover if we already have "where T:enum" then we'll strongly not want to add any other enum-like constraint. (Well, lambda syntax was introduced in addition to delegate syntax, but that was special and important...)
Nov 20, 2014 at 6:20 PM
lwischik wrote:
Halo_Four wrote:
It's hard to not have that attitude, though, given that the CLR supports these constraints as do other commercial CLR languages, especially without understanding the reasons that the C# team decided to impose the limitation. If I recall correctly supercat already did attempt to comment out the compiler errors and it did function as expected.
The danger is that if we unblock it now with the syntax "where T:enum", and it proves not quite to hit the spot, then we'll be forever unable to use that syntax for a more useful enum constraint. Moreover if we already have "where T:enum" then we'll strongly not want to add any other enum-like constraint. (Well, lambda syntax was introduced in addition to delegate syntax, but that was special and important...)
Understood, particularly given that in this proposal I did go straight for the enum and delegate keywords rather than respective type names. I am also currently looking at it from the perspective of supporting only what the CLR supports today and not supporting additional functionality.

Given that I absolutely understand that even the simplest of cases wouldn't happen in the C# 6.0 timeframe I'd love to sit back and see how constraint support can be expanded, especially now that I've seen how many use cases the F# team have tackled (arithmetic operations would make a lot of people happy). But I personally would feel more comfortable having the CLR team expand on constraints rather than do something like using attributes as a convention to declare the constraint. This is particularly poignant I think with the JIT where it could make better decisions based on the types in question so that, for an + constraint if the generic type parameter is int that it generates the appropriate and most efficient native code despite whatever the IL does.

Beyond that, I'd love more commentary from yourself as to the content of this proposal and the conversations thus far. I went for what I felt was a good middle ground of functionality which has exposed a couple of potential problems but with potential rewards.
Nov 20, 2014 at 6:29 PM
Halo_Four wrote:
Actually I think you are taking this further than I had intended. The example code, as written, should always emit the IL conv.i4 as the developer specified a cast to an int. If the underlying type of the enum was an int then the conversion would be effectively a no-op and if the underlying type was a long the value would be truncated. This is consistent with the behavior today if the programmer casts from an arbitrary enum to an int, or any of the other integral types.
I hope that when you say that I'm taking this further than you intended you don't mean that you want to disallow a GetValue that returns long/ulong instead of int. I doubt that anyone in the language design team would accept such a truncated implementation.

And if you want to allow casts to long/ulong then you need to emit a conv.i8/conv.u8 instruction. And the choice of instruction is determined by the source type - conv.i8 if the source type is signed and conv.u8 if the source type is unsigned.
Nov 20, 2014 at 6:55 PM
mdanes wrote:
Halo_Four wrote:
Actually I think you are taking this further than I had intended. The example code, as written, should always emit the IL conv.i4 as the developer specified a cast to an int. If the underlying type of the enum was an int then the conversion would be effectively a no-op and if the underlying type was a long the value would be truncated. This is consistent with the behavior today if the programmer casts from an arbitrary enum to an int, or any of the other integral types.
I hope that when you say that I'm taking this further than you intended you don't mean that you want to disallow a GetValue that returns long/ulong instead of int. I doubt that anyone in the language design team would accept such a truncated implementation.

And if you want to allow casts to long/ulong then you need to emit a conv.i8/conv.u8 instruction. And the choice of instruction is determined by the source type - conv.i8 if the source type is signed and conv.u8 if the source type is unsigned.
Not at all. What I intended to demonstrate there was that a developer of such a method could cast the instance of the generic type parameter to any of the integral types, but they would have to specify the integral type that they wanted to cast it to. This is inherently non-generic and probably of little use, but it is allowed by the language now regardless of the underlying type of the enum.
Nov 20, 2014 at 7:02 PM
Halo_Four wrote:
Not at all. What I intended to demonstrate there was that a developer of such a method could cast the instance of the generic type parameter to any of the integral types, but they would have to specify the integral type that they wanted to cast it to. This is inherently non-generic and probably of little use, but it is allowed by the language now regardless of the underlying type of the enum.
It seems that you're missing the point of my comment. The type of conversion that needs to be performed is determined by both the source and the destination type. The destination type is known (int in your example), the source type is unknown - it can be any of the integral types, signed or unsigned.
Nov 20, 2014 at 7:06 PM
Edited Nov 20, 2014 at 7:08 PM
mdanes wrote:
Halo_Four wrote:
Not at all. What I intended to demonstrate there was that a developer of such a method could cast the instance of the generic type parameter to any of the integral types, but they would have to specify the integral type that they wanted to cast it to. This is inherently non-generic and probably of little use, but it is allowed by the language now regardless of the underlying type of the enum.
It seems that you're missing the point of my comment. The type of conversion that needs to be performed is determined by both the source and the destination type. The destination type is known (int in your example), the source type is unknown - it can be any of the integral types, signed or unsigned.
Why does the source type need to be known? The opcode doesn't care what the source is, only what the target should be. conv.i4 can be applied to any of them and the runtime handles it fine. If the source is a uint, long or ulong outside of the range of int the value is truncated which isn't optimal, but that's exactly what happens today if you cast from an arbitrary enum to an int.

To note, allowing arbitrary casting from T : enum to any of the integral types is on my list of "spaghetti thrown at the wall." I'm not offended if it doesn't stick and I'd happily cut it if it doesn't make sense or is more trouble than it might be worth.
Nov 20, 2014 at 7:24 PM
Edited Nov 20, 2014 at 7:24 PM
Halo_Four wrote:
Why does the source type need to be known? The opcode doesn't care what the source is, only what the target should be.
That's just not how integer conversions work. There are 3 kind of conversions:
  • From a source type with N bits to a destination type with less than N bits - truncate. The upper bits are simply discarded, the signedness of the types is irrelevant.
  • From a signed source type with N bits to a destination type with more than N bits - sign extend. The sign bit of the source value is copied into the upper bits of the destination value.
  • From a unsigned source type with N bits to a destination type with more than N bits - zero extend. The upper bits of the destination value are filled with 0.
Note that the signedness of the destination type is always irrelevant. If you convert int to long then sign extension is performed. If you convert int to ulong sign extension is performed too, the result is bogus if the source int value is negative (and it would be bogus too even if you zero extend).
To note, allowing arbitrary casting from T : enum to any of the integral types is on my list of "spaghetti thrown at the wall." I'm not offended if it doesn't stick and I'd happily cut it if it doesn't make sense or is more trouble than it might be worth.
Yes, but it's possible that you also need some conversion operations if you want to allow ^ & ^ ~ == and !=. I used your GetValue example because it was the easiest way to show that generic conversions are a bit of a problem.
Nov 20, 2014 at 7:48 PM
mdanes wrote:
Halo_Four wrote:
Why does the source type need to be known? The opcode doesn't care what the source is, only what the target should be.
That's just not how integer conversions work. There are 3 kind of conversions:
  • From a source type with N bits to a destination type with less than N bits - truncate. The upper bits are simply discarded, the signedness of the types is irrelevant.
  • From a signed source type with N bits to a destination type with more than N bits - sign extend. The sign bit of the source value is copied into the upper bits of the destination value.
  • From a unsigned source type with N bits to a destination type with more than N bits - zero extend. The upper bits of the destination value are filled with 0.
Note that the signedness of the destination type is always irrelevant. If you convert int to long then sign extension is performed. If you convert int to ulong sign extension is performed too, the result is bogus if the source int value is negative (and it would be bogus too even if you zero extend).
To note, allowing arbitrary casting from T : enum to any of the integral types is on my list of "spaghetti thrown at the wall." I'm not offended if it doesn't stick and I'd happily cut it if it doesn't make sense or is more trouble than it might be worth.
Yes, but it's possible that you also need some conversion operations if you want to allow ^ & ^ ~ == and !=. I used your GetValue example because it was the easiest way to show that generic conversions are a bit of a problem.
Oops, I just compiled/disassembled a bunch of conversion examples and I do see your point. I guess that does chuck out large swaths of logical and comparison code from being usable which is a real shame given the massive performance benefits.
Nov 20, 2014 at 8:02 PM
Fwiw we in Oxygene/RemObjects C# (http://remobjects.com/elements) just allow people to constrain on System.Enum, System.MulticastDelegate and friends, which could be a first step that doesn't break anything and doesnt' close the door for future expansion. There are real uses for constraining on delegates and enums.
Nov 20, 2014 at 9:20 PM
lwischik wrote:
The danger is that if we unblock it now with the syntax "where T:enum", and it proves not quite to hit the spot, then we'll be forever unable to use that syntax for a more useful enum constraint. Moreover if we already have "where T:enum" then we'll strongly not want to add any other enum-like constraint. (Well, lambda syntax was introduced in addition to delegate syntax, but that was special and important...)
I personally would not favor where T:enum or where T:delegate, but instead where T:System.Enum [optionally with a struct constraint as well] and where T:System.Delegate or where T:System.MulticastDelegate, on the basis that:
  1. Such constructs would have a clear and unambiguous meaning consistent with the remainder of C# and VB.NET.
  2. Such constructs (using the type names rather than keywords) would not be the most natural expression of any other concepts the language maintainers might wish to add in future [having where T:System.Enum behave like any other constraint type would not foreclose the possibility of where T:enum doing something different].
  3. Any potential difficulties which might arise from the existence of a class or method which implements such a constraint will arise whether or not C# or VB.NET allows such constraints, since other languages do allow them.
  4. Even if facilities are added whereby a where T:enum constraint could allow code to use operators on generic enumerated types, many usage cases for such constraints would still require the use of the generic static-class technique for efficient implementation (e.g. methods to convert enumerated types to or from strings). In some such cases (e.g. converting from a string), it would make sense to disallow System.Enum itself as a type (which could be done by including a struct constraint along with System.Enum), but in others (e.g. when converting to a string) it would make sense to allow it (among other things, to allow interoperability with existing code which might be using System.Enum directly as a type for lack of anything better). Thus, future addition of a where T:enum constraint which would indicate that T was a concrete type would not eliminate the usefulness of specifying System.Enum as a constraint by itself.
  5. The primary benefit of the constraints is not to the classes or methods that impose them (though in the case of Delegate, having something constrained to Delegate would eliminate five of the six typecasts in a statement like (T)(Object)Delegate.Combine( (Delegate)(Object)d1, (Delegate)(Object)d2);), but rather to all the other code which would be able to use the kinds of helper methods that such constraints would make practical.
There's only one place where I can see some potential quirkiness within a C# or VB.NET method which implements such constraints, specifically the interaction of those constraints with things like IConvertible. Since System.Enum itself implements IConvertible, any type which satisfies a System.Enum constraint would satisfy the IConvertible constraint as well, but adding the constraint would increase the set of methods which could be invoked upon an enumerable type. I don't think that should pose any bigger problem with derivatives of System.Enum than with any other value type, but would represent the one area I can think of where allowing the construct might potentially cause trouble.
Nov 20, 2014 at 10:12 PM
supercat wrote:
lwischik wrote:
The danger is that if we unblock it now with the syntax "where T:enum", and it proves not quite to hit the spot, then we'll be forever unable to use that syntax for a more useful enum constraint. Moreover if we already have "where T:enum" then we'll strongly not want to add any other enum-like constraint. (Well, lambda syntax was introduced in addition to delegate syntax, but that was special and important...)
I personally would not favor where T:enum or where T:delegate, but instead where T:System.Enum [optionally with a struct constraint as well] and where T:System.Delegate or where T:System.MulticastDelegate, on the basis that:
  1. Such constructs would have a clear and unambiguous meaning consistent with the remainder of C# and VB.NET.
  2. Such constructs (using the type names rather than keywords) would not be the most natural expression of any other concepts the language maintainers might wish to add in future [having where T:System.Enum behave like any other constraint type would not foreclose the possibility of where T:enum doing something different].
  3. Any potential difficulties which might arise from the existence of a class or method which implements such a constraint will arise whether or not C# or VB.NET allows such constraints, since other languages do allow them.
  4. Even if facilities are added whereby a where T:enum constraint could allow code to use operators on generic enumerated types, many usage cases for such constraints would still require the use of the generic static-class technique for efficient implementation (e.g. methods to convert enumerated types to or from strings). In some such cases (e.g. converting from a string), it would make sense to disallow System.Enum itself as a type (which could be done by including a struct constraint along with System.Enum), but in others (e.g. when converting to a string) it would make sense to allow it (among other things, to allow interoperability with existing code which might be using System.Enum directly as a type for lack of anything better). Thus, future addition of a where T:enum constraint which would indicate that T was a concrete type would not eliminate the usefulness of specifying System.Enum as a constraint by itself.
  5. The primary benefit of the constraints is not to the classes or methods that impose them (though in the case of Delegate, having something constrained to Delegate would eliminate five of the six typecasts in a statement like (T)(Object)Delegate.Combine( (Delegate)(Object)d1, (Delegate)(Object)d2);), but rather to all the other code which would be able to use the kinds of helper methods that such constraints would make practical.
There's only one place where I can see some potential quirkiness within a C# or VB.NET method which implements such constraints, specifically the interaction of those constraints with things like IConvertible. Since System.Enum itself implements IConvertible, any type which satisfies a System.Enum constraint would satisfy the IConvertible constraint as well, but adding the constraint would increase the set of methods which could be invoked upon an enumerable type. I don't think that should pose any bigger problem with derivatives of System.Enum than with any other value type, but would represent the one area I can think of where allowing the construct might potentially cause trouble.
In light of the conversations that have happened in this thread I am actually inclined to agree with you. Allow for the constraints against the actual types System.Enum (with or without struct) as well as System.Delegate and System.MulticastDelegate. I figured that I'd shoot for additional compiler functionality on top of simply having the compiler emit the constraint but it does appear that in many of the cases there are additional concerns.

As mentioned I don't imagine that any motion on this subject will occur within the C# 6.0 timeframe and as such I'm more than willing to continue the conversation about generic constraints and what can be done with them beyond removing these restrictions. I am particularly interested in how the CLR could officially support the constraints permitted by F# (and I think you would be too since that would open arithmetic operations up.)
Nov 20, 2014 at 10:47 PM
Halo_Four wrote:
As mentioned I don't imagine that any motion on this subject will occur within the C# 6.0 timeframe and as such I'm more than willing to continue the conversation about generic constraints and what can be done with them beyond removing these restrictions. I am particularly interested in how the CLR could officially support the constraints permitted by F# (and I think you would be too since that would open arithmetic operations up.)
What does F# allow, and do you have any idea what it does to allow it?

I would certainly like to see more different kinds of constraints available if I thought they would be practical in the existing .NET environment, but there are a few places where the designers of .NET painted themselves into a corner. Not to disparage the design in any way, since the design whose complications can be 100% foreseen are generally those which also have no benefits which were unforeseen by their creators, and thus go nowhere. Instead merely noting that the design of the underlying environment will affect a language. It's sometimes possible for languages to use an abstraction which differs from the environment (e.g. .NET doesn't have out parameters, but C# does), but such abstractions often end up being leaky (e.g. an implementation of e.g. IDictionary<TK,TV>.TryGetValue written in VB.NET could leave Value unmodified if a key isn't found; if a struct constructor written in C# calls such a method on one of the struct fields, the statement myStruct = new myStructType(someParams); may--even though it looks like an assignment--may leave some or all of myStruct untouched.]

An inherent limitation of .NET constraints is that .NET assumes that if none of T, U, or V includes one of the three hard-wired "special" constraints, and if T satisfies U and U satisfies V, then T will satisfy V. Unless delegates are given a default constructor (that would e.g. return a delegate that would either do nothing for methods returning void, or simply return default(T) for methods returning T) I see no way to define a constraint that would restrict things to concrete delegate types. It would be nice if there were some way of applying such a restriction, but I don't see any room for it in the type system and consequently wouldn't expect a language could implement it without some leakiness of abstraction.
Coordinator
Nov 20, 2014 at 10:56 PM
Halo_Four wrote:
I am particularly interested in how the CLR could officially support the constraints permitted by F# (and I think you would be too since that would open arithmetic operations up.)
Just to check I've understood - the CLR support you hope for is to enable the scenario of fast efficient algorithms on enums, related to the conv.i4 discussions?

It feels like there's another angle on this - static interfaces. If an interface were allowed to have static members like op_Add, and if enums by default implemented this interface with the appropriate bitsize, then you could write the efficient enum algorithms inside a method "void f<T>(T x) where T:INumeric".



PS. I want to say a huge thanks to everyone in this thread. I wasn't aware of half the points you all raised. I still believe that the "scenarios / use-cases" is the single most important part of the proposal and needs more attention. If anyone can find cases in your code or others' where you'd wanted to write generic methods over enums and couldn't, please write more scenarios. I have a kind of naive optimism that every problem can be fairly easily solved in the compiler and CLR so long as we understand exactly what the problem is :)
Nov 20, 2014 at 11:37 PM
lwischik wrote:
Halo_Four wrote:
I am particularly interested in how the CLR could officially support the constraints permitted by F# (and I think you would be too since that would open arithmetic operations up.)
Just to check I've understood - the CLR support you hope for is to enable the scenario of fast efficient algorithms on enums, related to the conv.i4 discussions?
Currently F# has support for operator constraints, even supporting scenarios where you might have multiple generic type arguments. I'd love to see formal CLR support for describing and enforcing these constraints which all managed languages can support.

For example, the two following F# generic methods require that the generic types support the + operator.
let inline add(value1 : ^T when ^T : (static member (+) : ^T * ^T -> ^T), value2: ^T) =
    value1 + value2

// ^T and ^U must support operator +
let inline heterogenousAdd(value1 : ^T when (^T or ^U) : (static member (+) : ^T * ^U -> ^T), value2 : ^U) =
    value1 + value2
I think that if the CLR could formally describe and the runtime enforce those constraints that would solve a number of the existing problems, especially with numeric types and generics.
It feels like there's another angle on this - static interfaces. If an interface were allowed to have static members like op_Add, and if enums by default implemented this interface with the appropriate bitsize, then you could write the efficient enum algorithms inside a method "void f<T>(T x) where T:INumeric".
Another possibility, although it feels very old-school Delphi. For non-operators would the members be invocable via T.StaticMethod()? F# does already support defining constraints that the generic type must have a static member of the specified type and signature:
// Member constraint with static member
type Class4<'T when 'T : (static member staticMethod1 : unit -> 'T) > =
    class end
Although that would probably get obnoxious if you wanted to support a bunch of different methods.
PS. I want to say a huge thanks to everyone in this thread. I wasn't aware of half the points you all raised. I still believe that the "scenarios / use-cases" is the single most important part of the proposal and needs more attention. If anyone can find cases in your code or others' where you'd wanted to write generic methods over enums and couldn't, please write more scenarios. I have a kind of naive optimism that every problem can be fairly easily solved in the compiler and CLR so long as we understand exactly what the problem is :)
No kidding.

As I've mentioned I don't think that there are a lot of use-cases for these constraints. It's mostly better implementations of what exists in the runtime, like Enum.HasFlags, or support for helpers which improve how code can be written today. If support for the raw constraint was added to the compiler and a few people contributed decent functioning code to the .NET Core project the subject would probably never/rarely come up again.

As I've mentioned, my use-cases, for which I've already written helper methods that work but aren't as type safe as they could be, are as follows:
  • Improved string to enum parsing, supporting strings of numeric values or combined flags separated by pipe, comma or whitespace.
  • Method to take a flags enum and return an enumerable of the individual flags
  • Helper method to subscribe an event field using the same Combine/CompareExchange loop that C# emits in the default event accessor methods
  • Methods to create weak delegates as well as subscribe weak delegates to events (allows subscriber to be collected)
  • Method to take a delegate and return an enumerable of the invocation list, recursively unwrapping target delegates
  • Method to raise an event in a ref field by enumerating over the invocation list and invoking the targets individually
    ** Methods to handle exceptions raised by individual invocations that can be handled or ignored
    ** Methods to conditionally invoke the methods until a condition is met, e.g. CancelEventArgs.Cancel == true
    ** Methods to raise the event async
Nov 20, 2014 at 11:49 PM
Edited Nov 21, 2014 at 12:01 AM
supercat wrote:
What does F# allow, and do you have any idea what it does to allow it?
Check 'em out:

F# Constraints

The short list of additions are:
  • Type is a delegate of the specified signature, e.g. delegate<obj * System.EventArgs, unit>
  • Type is an enum of the specified underlying type, e.g. enum<int32>
  • Type supports instance member of given signature
  • Type supports static member of given signature
  • Type supports operator with given parameters, can include other generic types as operands or return values, e.g. U = T + U
  • Type is comparable or an array of comparables
Unfortunately as far as I can tell this information is stripped from the assembly when compiled. ildasm.exe doesn't show them as either constraints or as attributes and in most cases the generic types themselves are unbound. For example, trying to call the example method heterogenousAdd with types that don't support the addition operator throws a TypeInitializationException at runtime when attempting to call a helper method which does the heavy lifting of supporting the operation.
Nov 20, 2014 at 11:52 PM
carlokok wrote:
Fwiw we in Oxygene/RemObjects C# (http://remobjects.com/elements) just allow people to constrain on System.Enum, System.MulticastDelegate and friends, which could be a first step that doesn't break anything and doesnt' close the door for future expansion. There are real uses for constraining on delegates and enums.
Thanks for the mention. Also thanks for the conversation regarding the logical comparisons that I was doing earlier. It performed so well that it's a shame that it's not technically correct and that other runtime implementations could behave differently. I only tested this on the .NET 4.5.2 runtime. It would be a shame if it caused RyuJIT to throw E_HONDA*.

* - If you get this you should feel ashamed.
Nov 21, 2014 at 12:39 AM
lwischik wrote:
It feels like there's another angle on this - static interfaces. If an interface were allowed to have static members like op_Add, and if enums by default implemented this interface with the appropriate bitsize, then you could write the efficient enum algorithms inside a method "void f<T>(T x) where T:INumeric".
There's another thread where we talked about that: https://roslyn.codeplex.com/discussions/570704

My first impression was that it would raise all kinds of issues, but maybe I was looking at it from the wrong angle... having an INumeric interface on enums and numeric primitive types would be really cool.
Nov 21, 2014 at 12:47 AM
tom103 wrote:
lwischik wrote:
It feels like there's another angle on this - static interfaces. If an interface were allowed to have static members like op_Add, and if enums by default implemented this interface with the appropriate bitsize, then you could write the efficient enum algorithms inside a method "void f<T>(T x) where T:INumeric".
There's another thread where we talked about that: https://roslyn.codeplex.com/discussions/570704

My first impression was that it would raise all kinds of issues, but maybe I was looking at it from the wrong angle... having an INumeric interface on enums and numeric primitive types would be really cool.
My problem with that, assuming the constraints of today's language and CLR, is that INumeric would have to define the operators as instance methods. The generic methods would then have to box any primitive type to the interface and call the virtual method. It would work, but the performance would be in the crapper.

If the CLR supported a form of static interfaces that would eliminate the box and the virtual call, but it would still be a method call. It's possible that the JIT could be smart enough to either inline that method call or to eliminate it entirely if the generic types are the known primitives. The same concerns and possibilities are true if the CLR officially supported static member/operator constraints like F#.
Nov 21, 2014 at 8:38 AM
Halo_Four wrote:
Unfortunately as far as I can tell this information is stripped from the assembly when compiled. ildasm.exe doesn't show them as either constraints or as attributes and in most cases the generic types themselves are unbound. For example, trying to call the example method heterogenousAdd with types that don't support the addition operator throws a TypeInitializationException at runtime when attempting to call a helper method which does the heavy lifting of supporting the operation.
Of course you don't see the constraints in ildasm since the CLR doesn't support such constraints :). And why does it throw an exception at runtime, isn't the constraint supposed to reject types without a + operator at compile time?!
The generic methods would then have to box any primitive type to the interface and call the virtual method. It would work, but the performance would be in the crapper.
Nope, that's not the case. When value type generic parameters are involved the JIT compiler generates distinct code for each value type. That is, the code is specialized for a particular value type. There's no need for any boxing nor virtual/interface calls. This also means that inlining is possible. It's actually possible to use operators in generic methods today and do so efficiently but it requires a bunch of boilerplate code and it has various limitations. I probably posted this before and others have done so in other places (but some tend to ignore the performance aspects) but here it is:
interface ICalculator<T> {
    T Add(T x, T y);
}

struct Int32Calculator : ICalculator<int> {
    private int dummy; // workaround for JIT bug
    public int Add(int x, int y) { return x + y; }
}

struct Number<T, TCalc> where TCalc : struct, ICalculator<T> {
    T value;
    public static implicit operator Number<T, TCalc>(T value) { return new Number<T, TCalc> { value = value }; }
    public static implicit operator T(Number<T, TCalc> number) { return number.value; }
    static TCalc Calc { get { return new TCalc(); } }
    public static Number<T, TCalc> operator +(Number<T, TCalc> x, Number<T, TCalc> y) { return Calc.Add(x, y); }
}

static T Sum<T, TCalc>(IEnumerable<T> values) where TCalc : struct, ICalculator<T> {
    var sum = new Number<T, TCalc>();
    // the += simply generates a x86 'add' instruction
    // there's no boxing, no calls and no objects are created on the heap (except the enumerator of course)
    // it is possible that the JIT compiler will fail inlining for methods more complex that Sum
    foreach (var value in values) sum += value;
    return sum;
}

static void Main() {
    Console.WriteLine(Sum<int, Int32Calculator>(new[] { 1, 2, 3 }));
}
This is nothing more than an adaptation of the traits technique that's sometimes used with C++ templates. The JIT workaround in Int32Calculator tells you that the JIT authors never thought of such a thing, structs with no fields cause the JIT compiler to generate weird code.


tim 103 wrote:
My first impression was that it would raise all kinds of issues, but maybe I was looking at it from the wrong angle... having an INumeric interface on enums and numeric primitive types would be really cool.
It does raise all kinds of issues. The conversion issues are likely to be more complicated than in the case of enums. For example you can try to change my above example to use byte instead of int. Will it produce the same result as a non generic implementation? Is the result still the same if the numeric expression is more complex than a simple addition? If the result is not the same is there a way to fix it?

Not to mention the problem of numeric constants. OK, let's say you get INumeric, operators in interfaces etc. How do write the following trivial method as a generic method that works for all numeric types?
int ScaleBy42(int x) { return x * 42; }
Nov 21, 2014 at 11:40 AM
Edited Nov 21, 2014 at 11:42 AM
One idea would be to allow static methods in interfaces (That the target class has to implement), since operators are static methods too this would work with any type (doing any operator on a set of classes), the runtime could convert a call to a simple op if it knows it's an integer type.
Nov 21, 2014 at 2:02 PM
Edited Nov 21, 2014 at 2:03 PM
mdanes wrote:
Halo_Four wrote:
Unfortunately as far as I can tell this information is stripped from the assembly when compiled. ildasm.exe doesn't show them as either constraints or as attributes and in most cases the generic types themselves are unbound. For example, trying to call the example method heterogenousAdd with types that don't support the addition operator throws a TypeInitializationException at runtime when attempting to call a helper method which does the heavy lifting of supporting the operation.
Of course you don't see the constraints in ildasm since the CLR doesn't support such constraints :). And why does it throw an exception at runtime, isn't the constraint supposed to reject types without a + operator at compile time?!
This was from a C# app calling the F# methods. C# had no clue about the constraints at compile time. I imagine F# does enforce them, but I'm really rusty at F# and haven't confirmed.
This is nothing more than an adaptation of the traits technique that's sometimes used with C++ templates. The JIT workaround in Int32Calculator tells you that the JIT authors never thought of such a thing, structs with no fields cause the JIT compiler to generate weird code.
That's neat. I'm sure that the JIT authors didn't directly consider that. Did you try in RyuJIT? Also, if I recall correctly, C# automatically emits a byte field to an empty struct as I think the CLR requires structs to have some allocated size. That may be why it the JIT misses that potential optimization.
Nov 21, 2014 at 3:20 PM
I would like to see languages like C# and VB.NET include something a bit like C++ templates as a compile-time feature, but with a requirement that all types for which a template would be used must be specified in the source. Thus, something like [at class scope; syntax chosen arbitrarily]:
T sum<T using {Int32, UInt32, Int64, UInt64}>(T param1, T param2)
{
  return param1 + param2;
}
would be semantically equivalent to:
Int32 sum(Int32 param1, Int32 param2) { return param1 + param2; }
UInt32 sum(UInt32 param1, UInt32 param2) { return param1 + param2; }
Int64 sum(Int64 param1, Int64 param2) { return param1 + param2; }
UInt64 sum(UInt64 param1, UInt64 param2) { return param1 + param2; }
Templates wouldn't offer the same semantic advantages as C# generics, but unlike generics they would have no problem providing compatibility with operators. More importantly, the JITter would be able to optimize and inline them fully in ways that would not be possible when using generics. Further, it would be possible to do things like:
T FindLeastBit<T using {Int16, Int32, Int64 etc.}(T it)
{
  if (it == T.MinValue) return T.MinValue;
  return it & (T)(it-1);
}
[yes, I know one could simply use unchecked arithmetic there; my point is that while there would be no way for generics to use a constant like T.MinValue and have its value get folded into the code, a template feature could do that easily].
Nov 21, 2014 at 3:38 PM
Edited Nov 21, 2014 at 3:41 PM
carlokok wrote:
One idea would be to allow static methods in interfaces (That the target class has to implement), since operators are static methods too this would work with any type (doing any operator on a set of classes), the runtime could convert a call to a simple op if it knows it's an integer type.
There are three possible meanings I can see for having static members in interfaces:
  1. Utility methods, properties, etc. within an interface would eliminate the need for code to use static helper classes like Enumerable<T> with interfaces like IEnumerable<T>. Allowing code to say IEnumerable<T>.Empty would be cleaner than requiring Enumerable<T>.Empty.
  2. One I've long wished for: the ability to have an interface designate that certain static methods should be used by the type loader in a fashion somewhat analogous to extension methods; whether these static methods should themselves reside within the interface directly, within a nested class, or within one or more classes listed in an attribute attached to the interface would be a reasonable topic of discussion. A usage case of this is given below.
  3. Virtual static methods for which classes that implement the interface would be allowed to provide implementations. It would be possible for a languages to implement virtual static methods with reasonable semantics without requiring additional support in the Runtime, assuming language designers could agree upon the exact details of implementation, by having any class which declares virtual static members define a compiler-generated nested class to hold such members, and a not-necessarily-nested static generic class to hold a singleton reference to an instance of the above. If T is constrained to ClassWithGenericFoo, then T.Foo() would be equivalent to ClassWithGenericFoo.__StaticHelperHolder<T>.Helper.Foo(); Note that static classes could be virtual but not abstract, since there would be no mechanism by which a generic argument could be constrained to types which provide implementations for all appropriate static members.
With regard to #2, such a feature would allow a newer version of an interface like IEnumerator to add a member:
Int32 Move(Int32 distance);
provided that the interface designated a couple of static methods:
static Int32 Move<T>(T it, Int32 Distance) where T:class,IEnumerator
{
  while(Distance > 0 && it.MoveNext())
    Distance--;
  return Distance;
}
static Int32 Move<T>(ref T it, Int32 Distance) where T:struct,IEnumerator
{
  while(Distance > 0 && it.MoveNext())
    Distance--;
  return Distance;
}
I think the methods would probably have to be allowed to reside in different places because most languages don't allow two overloads to differ only in whether a parameter is passed by value or by ref. The intended semantics would be that if the type loader tries to read a type which claims to implement IEnumerator but it doesn't include int Move(int), the type loader would auto-generate a stub implementation that would call one of the two methods listed above, based upon whether the type in question was a struct or a class.

While I can certainly see some usefulness to being able to invoke static methods on constrained generic type parameters, I think the other uses for static methods in interfaces are more compelling.
Nov 21, 2014 at 4:31 PM
Halo_Four wrote:
I imagine F# does enforce them, but I'm really rusty at F# and haven't confirmed.
Probably it's best to think of F# constraints as being completely unrelated to CLR's generic constraints. Much like C++/CLI templates having nothing to do with CLR generics (and they too are language specific, you can't use a C++/CLI template from C#).
Also, if I recall correctly, C# automatically emits a byte field to an empty struct as I think the CLR requires structs to have some allocated size. That may be why it the JIT misses that potential optimization.
Yes, in theory an empty struct should have 1 byte, otherwise you end up with weird situations such as 2 array elements having the same address. But that's not what happens here. The JIT compiler figures out that the dummy field isn't used and eliminates the whole thing, that struct is never allocated/initialized. Without the dummy field the JIT compiler thinks that the struct has 16 bytes and initialize all those 16 bytes to 0 in a clumsy way. That makes no sense, it's a bug in the JIT compiler. And yes, RyuJIT does the same thing - it's based on the x86 JIT and it inherits some of its oddities.
Nov 21, 2014 at 4:41 PM
Halo_Four wrote:
  • Type is a delegate of the specified signature, e.g. delegate<obj * System.EventArgs, unit>
  • Type is an enum of the specified underlying type, e.g. enum<int32>
  • Type supports instance member of given signature
  • Type supports static member of given signature
  • Type supports operator with given parameters, can include other generic types as operands or return values, e.g. U = T + U
  • Type is comparable or an array of comparables
It would have been nice if some of those things had been included in the Framework, but they haven't been. It sounds like what F# is doing is like a version of dynamic which performs binding when types are loaded. Such a thing does allow some more powerful semantics than simply using .NET generics in the fashion for which they were designed, but on the flip side I have a strong suspicion that it greatly reduces the JITter's ability to have different members of a generic family share the same JITted code. Further, if I were designing a language and wanted to allow such features while interacting cleanly with other .NET languages, I would strongly encourage any type which includes constraints beyond those supported in .NET to indicate what should happen given something that doesn't abide by the expected constraints. I've always considered TypeInitializationException to be evil. Given an invalid type T, I would think having each operation (not just the first for a given type) throw UnsupportedGenericTypesException derived from NotSupportedException would be better.

I guess my thought with C# is that I would like there to be a language which would make it possible to do just about anything which could be done in CIL, but would let one nearly all of the code in a higher-level language. The changes required to allow C# to play such a role would be relatively slight, and need not affect any existing C# code. Given that C# has added exception filters, the main obstacles I see that C# still poses are:
  1. Forbidding certain generic constraints (the current topic of discussion)
  2. Inability to control instance initialization sequence between child and parent classes.
  3. Imposition of its own pattern for Finalize code [generally not a huge problem, but unnecessary]
  4. Inability to declare a new virtual sealed method (to ensure that all derivative classes use the same implementation as the base; doing this with Finalize would let a class guard itself against unwanted resurrection by subclasses.
Of these, the most important one IMHO is number 2; one of the major things I want in any "primary constructor" proposal would be a means by which a derived class could perform non-trivial initialization before the base class. It shouldn't be hard for a language to provide such control, and I see no particular reason C# shouldn't.

For things which could only be supported through the use of wrapper code, having a language implement such wrapper code automatically (as is done with e.g. iterators) is often more convenient than having to implement such constructs in user code, but in such cases almost anything the language could do could also be handled by user code (and a language's ability to do things user code could not would imply that the language has failed to expose a feature of the underlying environment). My primary interest doesn't lie with such features, but rather with having a language expose the underlying abilities of the environment necessary to make anything that's possible within the environment, possible within user code.
Nov 21, 2014 at 4:47 PM
mdanes wrote:
Halo_Four wrote:
I imagine F# does enforce them, but I'm really rusty at F# and haven't confirmed.
Probably it's best to think of F# constraints as being completely unrelated to CLR's generic constraints. Much like C++/CLI templates having nothing to do with CLR generics (and they too are language specific, you can't use a C++/CLI template from C#).
I totally agree. I wouldn't advocate supporting anything like them as they are today as they don't play well in the rest of the .NET ecosystem and don't provide compile-time type safety. I would love to see a conversation about supporting some of those constraints officially in the CLR and runtime at which point C# could officially provide support for them as well.
Dec 7, 2014 at 1:25 AM
I'm watching this C++ talk "Make Simple Tasks Simple!" by Bjarne Stroustrup from C++ Con 2014 and at time index 58:30 he is talking about "Concepts" for C++ templates. I like the idea that someone could write their own concepts which appear to me to be similar to generic constraints. This got me thinking if a future version of C#, using Roslyn's ability to hook into compilation, could allow folks to effectively write their own generic constraints or as Bjarne refers to them as - predicates. This would likely be an offshoot of efforts to enable meta-programming in C# but I think it would be useful.
Dec 7, 2014 at 11:25 PM
r_keith_hill wrote:
I'm watching this C++ talk "Make Simple Tasks Simple!" by Bjarne Stroustrup from C++ Con 2014 and at time index 58:30 he is talking about "Concepts" for C++ templates. I like the idea that someone could write their own concepts which appear to me to be similar to generic constraints. This got me thinking if a future version of C#, using Roslyn's ability to hook into compilation, could allow folks to effectively write their own generic constraints or as Bjarne refers to them as - predicates. This would likely be an offshoot of efforts to enable meta-programming in C# but I think it would be useful.
Roslyn can do it -anlyzers. FxCop can do it. Lots o tools and frameworks can do it.

But none will enforce that constraints at run time. And that's something a C# programmer is expecting.
Dec 9, 2014 at 3:49 PM
PauloMorgado wrote:
But none will enforce that constraints at run time. And that's something a C# programmer is expecting.
Actually, I think what C# programmers are expecting is that constraints will be enforced at compile time and--when using Reflection or code written in another language--at type creation time. The fact that a class Foo<T> where Tnew declaration means that no class Foo<Moo> can exist unless Moo has a parameterless constructor. To be sure, it's possible the constructor might throw an exception, but the Moo could safely be assumed to have a parameterless constructor.

That style of constraint enforcement is at the same time one of the neatest things about .NET type system, but is also unfortunately rather confining, since the design of the type system makes it essentially impossible to add any additional kinds of constraints which will behave in such fashion. For example, while it might be nice to have a constraint that would specify that a type features a public constructor that accepts a single parameter of some type, there's no mechanism for expressing such a thing in CLS. It would be possible for the Framework to define a marker interface void IHaveConstructorAcceptingTParam<in TParam,TResult> with the intention that the only instances that should be implement that interface would be those instances of a type type TResult which includes a single parameter of type TParam, and for compilers to then interpret:
class CachingFactory<TParam,TResult> where TResult : new(TParam)
{
  TResult Create(TParam param) { return new TResult(param); }
}
as
class CachingFactory<TParam,TResult> where TResult : IHaveConstructorAcceptingTParam<TParam, TResult>
{
  TResult Create(TParam param) { return ConstructorHelper<TParam,TResult>.CreateNew(param); }
}
but there would be no way to ensure that TResult actually included a constructor of the proper form and didn't simply implement the interface "manually" for some unknown reason.
Dec 10, 2014 at 12:04 AM
supercat wrote:
PauloMorgado wrote:
But none will enforce that constraints at run time. And that's something a C# programmer is expecting.
Actually, I think what C# programmers are expecting is that constraints will be enforced at compile time and--when using Reflection or code written in another language--at type creation time. The fact that a class Foo<T> where Tnew declaration means that no class Foo<Moo> can exist unless Moo has a parameterless constructor. To be sure, it's possible the constructor might throw an exception, but the Moo could safely be assumed to have a parameterless constructor.
I don't think I understood what you wrote but, I, as a C# developer, expect that the constraints are enforced at run time. And when does that enforcement happen? At construction time. Once it's constructed, the constraints are guaranteed to be valid.

But that's only for types.

For methods, I, as a C# developer, expect the constraints to be enforced as needed. I would say that it's when the JIT compiles caller code or when an invocation by reflection is made. There might be other case related to delegates, but I haven't given much thought about that.

And I expect the compiler to do a good static verification of the code and not emit binaries that are verifiable to fail on run time.
That style of constraint enforcement is at the same time one of the neatest things about .NET type system, but is also unfortunately rather confining, since the design of the type system makes it essentially impossible to add any additional kinds of constraints which will behave in such fashion. For example, while it might be nice to have a constraint that would specify that a type features a public constructor that accepts a single parameter of some type, there's no mechanism for expressing such a thing in CLS. It would be possible for the Framework to define a marker interface void IHaveConstructorAcceptingTParam<in TParam,TResult> with the intention that the only instances that should be implement that interface would be those instances of a type type TResult which includes a single parameter of type TParam, and for compilers to then interpret:
class CachingFactory<TParam,TResult> where TResult : new(TParam)
{
  TResult Create(TParam param) { return new TResult(param); }
}
as
class CachingFactory<TParam,TResult> where TResult : IHaveConstructorAcceptingTParam<TParam, TResult>
{
  TResult Create(TParam param) { return ConstructorHelper<TParam,TResult>.CreateNew(param); }
}
but there would be no way to ensure that TResult actually included a constructor of the proper form and didn't simply implement the interface "manually" for some unknown reason.
There have a very few cases where I felt I need that. But I usually end up with a 1:1 factory type to constructed instance type ratio.
Dec 10, 2014 at 12:28 AM
Edited Dec 10, 2014 at 12:31 AM
I'm going to go out on a limb and say that you're both saying the same thing. We definitely want the compiler to be the first step in enforcing that these constraints are valid, but we want the constraints to also be fully enforced by the runtime. If a generic type parameter declares a constraint that a type argument must have a static member of a specific signature then we would want C# to ensure that we cannot write code that attempts to use a type that does not conform to the constraint. But if someone manually emitted the IL of a concrete class with a generic type argument that does not meet the constraint the runtime should consider that assembly malformed and refuse to load it.

I'm also going to add that I've been writing a lot of Java lately and I absolutely hate type erasures. I wouldn't want C# to incorporate any voodoo like that that can be so easily outwitted and defeated resulting in odd and unexpected runtime errors.
Dec 10, 2014 at 4:57 PM
Halo_Four wrote:
I'm going to go out on a limb and say that you're both saying the same thing. We definitely want the compiler to be the first step in enforcing that these constraints are valid, but we want the constraints to also be fully enforced by the runtime. If a generic type parameter declares a constraint that a type argument must have a static member of a specific signature then we would want C# to ensure that we cannot write code that attempts to use a type that does not conform to the constraint. But if someone manually emitted the IL of a concrete class with a generic type argument that does not meet the constraint the runtime should consider that assembly malformed and refuse to load it.
The distinction between "compile time" and "run time" can be somewhat vague when dealing with types that are generated on the fly. I'm not sure what term would be most suitable, however, to express the concepts that:
  1. The validation occurs when types are loaded [which occurs at run-time], but...
  2. The validity of a type can be determined by examining a few invariants of other types.
I'm also going to add that I've been writing a lot of Java lately and I absolutely hate type erasures. I wouldn't want C# to incorporate any voodoo like that that can be so easily outwitted and defeated resulting in odd and unexpected runtime errors.
IMHO, the biggest problem with Java's implementation is that "generic" types have no general way of knowing their type. There are times in .NET when it would be useful to be able to say, e.g. "I have a list of references to IFoo, and I know that every single object identified by that list also implements IBar; I would like to pass each object in the list to a method which requires something that implements both IFoo and IBar. Unless all items of the list derive from a common type which implements IFoo and IBar, such a concept cannot be expressed at all in .NET without either using Reflection, or else having each item in the list stored in a wrapper of a generic type that includes the appropriate constraints. Being able to have such constraints tested at runtime rather than having to have them be statically verifiable would be helpful in such cases.

More generally, there are often advantages to having promises be rigidly enforced by the compiler and framework, but there are also often advantages to having promises which code is expected to uphold, even if they're not rigidly enforced, if it is understood that client code is entitled to rely upon such promises, and any code which would violate such promises in such a way as to violate client expectations is broken. For example, it would be useful to have an ImmutableList<T> which would promise that as long as it exists, it will contain or identify the same sequence of T values or objects. If such a type were derived from ReadOnlyList<T>, and had its constructor chain to its parent constructor with a copy of any passed-in sequence, it could guarantee that the list was immutable. On the other hand, if code has an object which implements IList<T>, which promises to always represent the same sequence of T values or objects, and which will actually uphold such a promise, copying all of those items to a new array would likely be a waste of effort. Copying to the array may be necessary if code can't afford to rely upon the promise, but otherwise it would be more efficient to take the object at its word. Having a compiler try to "discourage" actions that would break such a promise may be helpful, but recognizing that not all immutable lists will be statically-recognizable as such may be more helpful than a more strict enforcement of immutability.
Dec 10, 2014 at 5:13 PM
Halo_Four wrote:
But if someone manually emitted the IL of a concrete class with a generic type argument that does not meet the constraint the runtime should consider that assembly malformed and refuse to load it.
If one were to write in IL code equivalent to:
interface IFoo {};
interface IBar {};
class Blah<T>() where T:IFoo, IBar {}
class Boz<U>() { Blah<U> it; }
class DoesBoth : IFoo, IBar {}

is there any possible means (including disabling verification) by which code could construct type Boz<DoesBoth> or an instance thereof? I would expect the verifier to reject Boz<> if there could exist any type U for which Blah<U> (and thus Boz<U>) would be invalid; even if the verifier were bypassed, however, I'm not sure how code generation would work. Any idea?
Dec 10, 2014 at 9:46 PM
supercat wrote:
Halo_Four wrote:
But if someone manually emitted the IL of a concrete class with a generic type argument that does not meet the constraint the runtime should consider that assembly malformed and refuse to load it.
is there any possible means (including disabling verification) by which code could construct type Boz<DoesBoth> or an instance thereof? I would expect the verifier to reject Boz<> if there could exist any type U for which Blah<U> (and thus Boz<U>) would be invalid; even if the verifier were bypassed, however, I'm not sure how code generation would work. Any idea?
Fairly sure that all C# generics are a subset of what is supported and enforced by the runtime.

The following is legal C# code currently:
interface IFoo { }
interface IBar { }
public class FooBar : IFoo, IBar { }
public class Blah<T> where T : IFoo, IBar { }

public class Boz<U> {
    public Blah<U> it { get; set; }
}

static class Program {
    static void Main() {
        Boz<FooBar> x = new Boz<FooBar>();
        Boz<int> y = new Boz<int>();

        // perfectly legal
        x.it = new Blah<FooBar>();
        // only legal call is to pass null, compiler balks otherwise
        y.it = null;
    }
}
If you were to force it by modifying the IL to call y.it = new Blah<int>() it would fail at runtime with a TypeLoadException.
Dec 11, 2014 at 10:10 AM
Halo_Four wrote:
The following is legal C# code currently:
interface IFoo { }
interface IBar { }
public class FooBar : IFoo, IBar { }
public class Blah<T> where T : IFoo, IBar { }

public class Boz<U> {
    public Blah<U> it { get; set; }
}

static class Program {
    static void Main() {
        Boz<FooBar> x = new Boz<FooBar>();
        Boz<int> y = new Boz<int>();

        // perfectly legal
        x.it = new Blah<FooBar>();
        // only legal call is to pass null, compiler balks otherwise
        y.it = null;
    }
}
Actually, it's not. Compiler will tell you that U in Boz cannot be used as T type parameter when declaring it field. You need to propagate constraints. And if you do, then compiler will tell you that Boz<int> is invalid, as int does not satisfy them
Dec 11, 2014 at 1:20 PM
Edited Dec 11, 2014 at 1:25 PM
Przemyslaw wrote:
Halo_Four wrote:
The following is legal C# code currently:
interface IFoo { }
interface IBar { }
public class FooBar : IFoo, IBar { }
public class Blah<T> where T : IFoo, IBar { }

public class Boz<U> {
    public Blah<U> it { get; set; }
}

static class Program {
    static void Main() {
        Boz<FooBar> x = new Boz<FooBar>();
        Boz<int> y = new Boz<int>();

        // perfectly legal
        x.it = new Blah<FooBar>();
        // only legal call is to pass null, compiler balks otherwise
        y.it = null;
    }
}
Actually, it's not. Compiler will tell you that U in Boz cannot be used as T type parameter when declaring it field. You need to propagate constraints. And if you do, then compiler will tell you that Boz<int> is invalid, as int does not satisfy them
That's what I thought, too. But this compiles just fine in Visual Studio 2013 and runs just fine on .NET 4.5.2.

What the compiler (and runtime) both enforce is that there is no way to actually create an instance of Blah<int> that you can assign to the it property. You can assign null to that property all day long.

What the compiler (and I assume runtime) does also enforce is that Boz<U> cannot internally instantiate an instance of Blah<U> since the U generic type doesn't have the same generic constraints as T on Blah<>.
Dec 11, 2014 at 2:37 PM
Halo_Four wrote:
That's what I thought, too. But this compiles just fine in Visual Studio 2013 and runs just fine on .NET 4.5.2.
Nope, this code does not compile in VS2013. It's as Przemyslaw said, those constraints need to be propagated and when you do that you won't be able to write Boz<int>.

And yes, the runtime does enforce the constraints, this piece of code will throw an exception at runtime:
typeof(Blah<>).MakeGenericType(typeof(int));
It has to be this way otherwise the system would no longer be type safe.
Dec 11, 2014 at 3:10 PM
mdanes wrote:
Nope, this code does not compile in VS2013. It's as Przemyslaw said, those constraints need to be propagated and when you do that you won't be able to write Boz<int>.
I would not expect the code to compile in C# or VB.NET; I'm curious, though, what would happen if such code came about e.g. because Blah was in another assembly, and had its constraint added after the assembly with Boz was compiled. I would expect that Boz would fail verification; if the verifier was disabled could Boz load but then cause runtime trouble if code tried to create a Boz<int>, or would the runtime choke on Boz even with the verifier disabled, and even if no effort was made to construct a Boz<U> type for any U which wouldn't satisfy Blah's constraints?

The exception thrown by Reflection occurs when setting up a data structure for the purpose of runtime code generation, supplying input for the JIT compiler. If one had a program under Unix which generated a .C file, compiled it, and ran it, an error in the generated C file might cause an execution-time error from the point of view of the program that was running the compiler, but it would be a compile-time error from the point of view of the code the outer program was seeking to generate.

BTW, is the inability to cast from one generic type to another without going via Object a CIL restriction or merely a language restriction? For any two generic types T and U, if u is a variable of type U, the compiler will allow (T)(object)u, which will perform a reference conversion if U is a class type which derives from T, perform a necessary boxing or unboxing operation if U is a value type and T is an interface which it implements or vice versa, or perform wasteful boxing and unboxing operations if T and U are the same value type. Being able to cast directly from T to U with the same semantics as cast-via-object in the first two cases, but without the boxing and unboxing in the third, would be helpful if the it could be done without extra code [If the only way to avoid boxing in the case where T and U are the same type would be to use Reflection to bind a static delegate or an object that behaves like one, it may be just as well to have user code be responsible for that. Such code could significantly improve performance when T and U are the same value type, but slightly degrade performance in the other scenarios; in programs where the "matching value type" scenario never occurs, adding code to provide for it would be a waste.]
Dec 11, 2014 at 5:36 PM
Edited Dec 11, 2014 at 5:40 PM
supercat wrote:
I'm curious, though, what would happen if such code came about e.g. because Blah was in another assembly, and had its constraint added after the assembly with Boz was compiled.
Simple, you get a TypeLoadException.
I would expect that Boz would fail verification; if the verifier was disabled could Boz load but then cause runtime trouble if code tried to create a Boz<int>, or would the runtime choke on Boz even with the verifier disabled, and even if no effort was made to construct a Boz<U> type for any U which wouldn't satisfy Blah's constraints?
This has nothing to do with verification. The verification you're talking about, the one that can be turned off, has to do with code, not with types.
The exception thrown by Reflection occurs when setting up a data structure for the purpose of runtime code generation, supplying input for the JIT compiler.
You're mixing up things. What code generation needs to occur in the example I presented? I'm not calling any methods on the involved types. I don't attempt to use the created type in a call to Activator.CreateInstance so you can't even claim that the constructor of the type needs to be JIT compiled. Maybe the static constructor would be called but that's not necessary and anyway I may not exist. I suppose this claim is a side effect of you confusing code verification with type system sanity.
BTW, is the inability to cast from one generic type to another without going via Object a CIL restriction or merely a language restriction?
I don't fully understand the scenario you have in mind so I'll try to provide an answer for the general case - reference types.

For reference types this is simple, the compiler and not the runtime requires the cast to object. This is normal, in the absence of a constraint that relates T to U the compiler has no way of knowing if the cast is valid and rejects. Want to get around the compiler - cast to object. Want to avoid casting to object - add the necessary constraint.

What really makes your question unclear is the fact that you drag in value types. Boxing or not, you simply can't cast one value type to another. And if T and U are the same then why do you have both T and U to begin with?
Dec 11, 2014 at 9:05 PM
mdanes wrote:
Halo_Four wrote:
That's what I thought, too. But this compiles just fine in Visual Studio 2013 and runs just fine on .NET 4.5.2.
Nope, this code does not compile in VS2013. It's as Przemyslaw said, those constraints need to be propagated and when you do that you won't be able to write Boz<int>.

And yes, the runtime does enforce the constraints, this piece of code will throw an exception at runtime:
typeof(Blah<>).MakeGenericType(typeof(int));
It has to be this way otherwise the system would no longer be type safe.
Crap, you're right. I swear I typed this into VS and it compiled/ran just fine. Oops!

Either way, I agree that the runtime should enforce these types of constraints, unlike Java. And if additional types of constraints are considered I would expect the same enforcement.
Dec 12, 2014 at 3:30 PM
mdanes wrote:
This has nothing to do with verification. The verification you're talking about, the one that can be turned off, has to do with code, not with types.
I don't know what degrees of semantic validation are enforced where; it sounds as though you're saying the type loader would reject the definition of type family Boz<U> even if all validation mechanisms that could be bypassed, were, and even if no effort were made to create a particular type for which Blah's constraint wouldn't hold?
What really makes your question unclear is the fact that you drag in value types. Boxing or not, you simply can't cast one value type to another. And if T and U are the same then why do you have both T and U to begin with?
Sometimes classes or methods can get invoked with the same type parameter in two spots. For example, one might have a method which accepts an IEnumerable<T> and a Predicate<U> and is supposed to return a List<U> of all items that satisfy the predicate; if the list contains items which aren't coercible to U, they should simply be regarded as not satisfying the predicate. For example, such a function could accept an IEnumerable<Animal> and a method LikesCatNip(Cat) and return a List<Cat> of containing all the cats for which LikesCatnip() would return true. Note if the list contains an instance of Dog, there should be no problem determining whether that qualifies as a "cat for which LikesCatnip() would return true" (the answer is "no, it isn't").

The most natural way of writing the inner loop for a general-purpose method as described would seem to be something like:
foreach (T it in sequence)
{
    if (it is U)
    {
      itAsU = (U)it; // Actually needs to be written itAsU = (U)(Object)it; 
      if (condition(itAsU))
        destList.Add(itAsU);
    }
  }
}
While it might be optimal to have a method to handle the particular case where T and U are the same type, there's no particular reason why the aforementioned method shouldn't be able to work decently with an IEnumerable<int> and a Predicate<int>. In such a case, the JITter would be able to optimize out it is U to always return true, but I don't think it can optimize out the boxing and unboxing required in the assignment of itAsU.
Dec 12, 2014 at 5:28 PM
supercat wrote:
it sounds as though you're saying the type loader would reject the definition of type family Boz<U> even if all validation mechanisms that could be bypassed, were,
Of course.
In such a case, the JITter would be able to optimize out it is U to always return true, but I don't think it can optimize out the boxing and unboxing required in the
assignment of itAsU.
It turns out the current JIT compiler does the opposite, it doesn't remove it is U but it does remove boxing. Removing boxing is actually trivial, even the most basic JIT compiler should be able to do it. Removing is should also be trivial, I suspect that the reason the JIT compiler doesn't do this is simply a missed optimization or some sort of bug.
Dec 13, 2014 at 5:21 AM
mdanes wrote:
It turns out the current JIT compiler does the opposite, it doesn't remove it is U but it does remove boxing. Removing boxing is actually trivial, even the most basic JIT compiler should be able to do it. Removing is should also be trivial, I suspect that the reason the JIT compiler doesn't do this is simply a missed optimization or some sort of bug.
I'm surprised it can eliminate the boxing, since it's sometimes possible to actually observe boxing behavior. Still, I guess in cases where it can determine the reference can't escape eliminating boxing would be a good thing. Otherwise, what machine instructions would the is instruction execute when run on a value type?
Dec 13, 2014 at 6:07 AM
supercat wrote:
I'm surprised it can eliminate the boxing, since it's sometimes possible to actually observe boxing behavior. Still, I guess in cases where it can determine the reference can't escape eliminating boxing would be a good thing.
Well, yes, in the general case you can't eliminate boxing but your (U)(object)t generates the sequence box T, unbox U. Due to the stack based nature of the IL it is obvious that the object produced by box is only used by unbox. And since we're talking about value types the JIT compiler generates specialized code and knows that T = U. In this case the box/unbox sequence is always a no-op. Unless you consider the potential OutOfMemoryException that box can throw to be a observable behavior :)
Otherwise, what machine instructions would the is instruction execute when run on a value type?
In the case of T = int and U = int it is U should produce no code. Again, the generic code is specialized for value types. Since the compiler eliminates boxing it obviously knows that T = U so it should also know that it is U is always true. And for the sake of example, if T = int and U = float then it is U is always false and the whole foreach loop is dead and can be eliminated.
Dec 13, 2014 at 5:32 PM
mdanes wrote:
In the case of T = int and U = int it is U should produce no code. Again, the generic code is specialized for value types. Since the compiler eliminates boxing it obviously knows that T = U so it should also know that it is U is always true. And for the sake of example, if T = int and U = float then it is U is always false and the whole foreach loop is dead and can be eliminated.
You wrote earlier that the current JIT didn't remove the "is" test, which I found confusing; the statement quoted above suggests that it does. Did I misunderstand your earlier statement, or does it apply in some scenarios but not others? There are a number of cases where a JIT compiler shouldn't need a type check:
  1. Both are matching non-generic values type (always true).
  2. Both are non-matching value types (always return false).
  3. T is non-generic value type, and U is Nullable<T> (always true).
  4. If U is non-generic value type, and T is Nullable<U>, then JIT code should simply test the value of the theT.HasValue backing field.
The only time I can see when the JITter should have to generate code to actually examine the type of a value type T would be in cases where either U is a reference type, or where T is itself a generic type with a type parameter of reference type. By my understanding (correct me if I'm wrong), although there are times when it would be more efficient to have a method generate distinct executable code when given type parameters of e.g. KeyValuePair<String,Object> and KeyValuePair<Object,Object>, the Runtime tries to avoid generating an excessive number of distinct executable methods, and instead calls one piece of executable code which receives information on the stack about the type of the objects it's receiving.
Dec 13, 2014 at 7:25 PM
supercat wrote:
You wrote earlier that the current JIT didn't remove the "is" test, which I found confusing; the statement quoted above suggests that it does.
I said that the JIT compiler doesn't remove the is and then I said that it should. Well, that was my intention for sure. Maybe I'm missing something, I'm a bit sleepy at this hour.

Indeed, there are a few other cases where is doesn't require a type check. But it seems that none of these cases are currently optimized by JIT. I guess I shouldn't be so surprised, unlike box/unbox the case of is is unfortunately far more complicated at IL level because there's no actual is instruction, IL's isinst behaves like as.
By my understanding (correct me if I'm wrong), although there are times when it would be more efficient to have a method generate distinct executable code when given type parameters of e.g. KeyValuePair<String,Object> and KeyValuePair<Object,Object>, the Runtime tries to avoid generating an excessive number of distinct executable methods, and instead calls one piece of executable code which receives information on the stack about the type of the objects it's receiving.
Yes, the code is shared between KVP<string,object>, KVP<object,object> and any other generic argument combination of reference types and yes, sometimes this sharing can lead to decreased performance. For example, x is T is a very fast check if T is known to be sealed but since the code is shared there's no way for the compiler to use the fast check, it doesn't know that T is sealed.

There's also the opposite situation, the code for some value types could be shared but currently that doesn't happen. For example, List<int>.Add and List<uint>.Add should produce identical code that can be shared without problems.
Dec 14, 2014 at 9:14 PM
mdanes wrote:
I said that the JIT compiler doesn't remove the is and then I said that it should. Well, that was my intention for sure. Maybe I'm missing something, I'm a bit sleepy at this hour.
Ah, your statement that the JIT should omit the is was in response to my question about what machine code that JIT would produce for the is for the simple value-type case; I'm still curious, if the JIT doesn't omit the is entirely in the simple value-type case, what does it generate? Does it invoke one of the Framework's internal type-checking routines, passing the same value type as both parameters? Or does it do a useless boxing operation?
Indeed, there are a few other cases where is doesn't require a type check. But it seems that none of these cases are currently optimized by JIT. I guess I shouldn't be so surprised, unlike box/unbox the case of is is unfortunately far more complicated at IL level because there's no actual is instruction, IL's isinst behaves like as.
I can see that could be a problem. One thing I've long thought would be nice would be a special form of type-check instruction which would behave as an assertion that a type met a particular criterion, thus allowing something like [proposed syntax isn't good, but I don't know what would be better]
bool AnyAcceptable<T,U>(IEnumerable<T> seq, Predicate<U> condition)
{
  if (T satisfies U)  // Within body of `if`, behave as though `U` had a `U` constraint
  {
    foreach(item in seq)
    {
      if (condition(item))
        return true;
    }
    return false;
  }
  else if (T overlaps U)
  {
    foreach(item in seq)
    {
      if (item is U && condition((U)(Object)item))
        return true;
    }
    return false;
  }
  else
    return false;
}
Note that the tests here would not be based upon the types of object instances thing1 and thing2, but rather upon the generic type parameters. Such a thing could be especially helpful if there were some attribute code could use to request that the jitter generate separate machine code for each combination of generic type parameters [even class-type ones]. There's nothing wrong with checking whether an IEnumerable<Animal> contains any items that satisfy a bool LikesCatnip(Cat) predicate, which would in turn imply that checking whether an IEnumerable<Dog> contains any items that satisfy that predicate would be legal, but given the aforementioned types there would really be no point in enumerating the sequence.

As it is, I think the best way to implement the above would be to use a static generic class with a delegate that uses one of the two iteration loops or else returns false unconditionally, but if CIL could allow code to be generated that would bypass the delegate dispatch or conditional logic that would be better.
There's also the opposite situation, the code for some value types could be shared but currently that doesn't happen. For example, List<int>.Add and List<uint>.Add should produce identical code that can be shared without problems.
The rules for array stores of integer types are quirky; even though an Int32 can be stored to a UInt32[] and vice versa, in general it would not be possible for a ValueType1 to be stored to a ValueType2 even if the types in question were layout-compatible. Thus, I would think the total number of redundant Add methods would be limited to about four; the extra JITter code required to eliminate such redundancy would probably exceed the total size of the redundant code that could be generated in most plausible scenarios.
Dec 16, 2014 at 5:19 AM
Edited Dec 16, 2014 at 5:19 AM
supercat wrote:
Note that the tests here would not be based upon the types of object instances thing1 and thing2, but rather upon the generic type parameters.
This probably belongs in a meta-programming bucket.
Such a thing could be especially helpful if there were some attribute code could use to request that the jitter generate separate machine code for each combination of generic type parameters [even class-type ones].
This can sometimes be done today with an additional generic parameter that's a struct.
There's nothing wrong with checking whether an IEnumerable<Animal> contains any items that satisfy a bool LikesCatnip(Cat) predicate, which would in turn imply that checking whether an IEnumerable<Dog> contains any items that satisfy that predicate would be legal, but given the aforementioned types there would really be no point in enumerating the sequence.
Yes, there's nothing wrong with that but it doesn't strike me as a very common thing to do. Adding features to a language in an attempt to optimize some not so common scenarios doesn't work too well.
The rules for array stores of integer types are quirky; even though an Int32 can be stored to a UInt32[] and vice versa, in general it would not be possible for a ValueType1 to be stored to a ValueType2 even if the types in question were layout-compatible...
This has nothing to do with the int/uint array quirk. But my choice of method was rather poor, List<T>.Add can't be easily shared because at some point it needs to instantiate a T[] and that code will be different for int and uint. But there are other methods that either don't depend on T at all (List<T>.Clear for example) and others that depend on T but the machine code is identical for certain Ts (List<T>.Find for example). For int/uint this happens because these types have the same size and same CPU registers and in many cases the same instructions are used to manipulate them. Now, unsigned types aren't commonly used in .NET but there are other combinations where sharing is possible - enums with the same underlying type, System.Drawing.Size/Point etc. Unfortunately this kind of optimization probably requires too much bookkeeping for a JIT compiler.
Dec 16, 2014 at 3:44 PM
mdanes wrote:
This probably belongs in a meta-programming bucket.
Perhaps, though what I've seen meta-programming tends to go more toward Turing-completeness, whereas I prefer to stick with things that are decidable.
Such a thing could be especially helpful if there were some attribute code could use to request that the jitter generate separate machine code for each combination of generic type parameters [even class-type ones].
This can sometimes be done today with an additional generic parameter that's a struct.
I find it interesting how many programming problems can be solved by adding an extra parameter that's not used for anything and simply takes a default value. Too bad there's no way to say "perform type inference and code generation as though this method takes a parameter of type X, but don't bother generating any code for it".
Yes, there's nothing wrong with that but it doesn't strike me as a very common thing to do. Adding features to a language in an attempt to optimize some not so common scenarios doesn't work too well.
It's not the most common scenario, but I chose it for its simplicity. Perhaps you would consider such a thing to be more "metaprogramming", but what I would think would be useful here and in some other scenarios would be a way of requesting overload selection based upon what criteria are met by a generic type T, whether that be expressed by if satisfies statements within a method, or by having several like-named method with different constraints that are marked as such overloads. This can be coded relatively efficiently using delegates (and is, for example, the way to write an efficient generic enum "has-flags" test) but it would be nice to have language support for it.
This has nothing to do with the int/uint array quirk. But my choice of method was rather poor, List<T>.Add can't be easily shared because at some point it needs to instantiate a T[] and that code will be different for int and uint. But there are other methods that either don't depend on T at all (List<T>.Clear for example) and others that depend on T but the machine code is identical for certain Ts (List<T>.Find for example). For int/uint this happens because these types have the same size and same CPU registers and in many cases the same instructions are used to manipulate them. Now, unsigned types aren't commonly used in .NET but there are other combinations where sharing is possible - enums with the same underlying type, System.Drawing.Size/Point etc. Unfortunately this kind of optimization probably requires too much bookkeeping for a JIT compiler.
Identifying that methods could be 100% shared probably wouldn't be too hard: observe what aspects of T the method cares about. Making the system efficient recognize, before generating code for a method, whether a method that was associated with a different type but was compatible had already been loaded, however, might require adding another level of indirection to the type-loading process. I don't know enough about the details to know what the tradeoffs would be, though.
Coordinator
Jan 25, 2015 at 4:08 PM
@Supercat / Halo_four / Olmo

I think I remember that one of you had written up a self-contained proposal about generic constraints somewhere, one that laid out the use-cases and incorporated stuff that had been discussed in this thread?

(or am I misremembering, and getting it confused with Olmo's proposal about non-nullable types? https://gist.github.com/olmobrutall/31d2abafe0b21b017d56 )


Anyway, as we kick off the language design process for C#7 this week, I'd love for us to be able to take your mostly-complete proposals and use them as the basis for discussion. That goes for everyone, of course, but the ones on non-nullable and generic-constraints were the most complete proposals I've seen so far, i.e. the most ready for us to work with.
Jan 26, 2015 at 11:01 PM
I don't think I was the one who wrote up the stand-alone proposal, though I'm probably the one that has written more code to show that even though it's not possible to do much with an System.Enum or System.Delegate constraint without using Reflection, the runtime cost of the Reflection required can be very low since it will only need to be done once for each different Enum or Delegate type. Other proposals have suggested adding some additional compiler complexity to support such constraints, but my proposal would be very simple: simply remove the two lines in the compiler which check for and forbid such constraints. Although there isn't so much a need to be able to use such constraints in general, there are some utility methods which could usefully have those constraints applied to them; I would expect that making the feature available in general would be cheaper and easier than making it available just for purposes of a few utility methods.

Personally, I subscribe to the theory that there should exist a language which allows code to be written in a high-level form if desired (rather than CIL), but which is also capable--to the extent practical--of expressing anything which is semantically meaningful in CIL. While there are a few significant holes in C# vs CIL (most notably the inability of C# to initialize class fields in any way that depends upon constructor parameters prior to chaining the base-class constructor), I would suggest that the restriction on enum/delegate constraints was most likely a result of the original authors of C# misinterpreting the extent to which CLR does and does not support such constraints. For the CLR to be deemed to fully "support" such constraints would require that it allow a T: struct,System.Enum to be used in all contexts that accept arbitrary value types derived from System.Enum, but the design of the CLR makes that impossible. The fact that a System.Enum constraint does not allow the CLR to use a type as though it were a known value type derived from System.Enum, however, does not mean that it cannot will not honor a System.Enum constraint in the same way as it would any other constraint. Simply put, I view the prohibition on System.Enum constraints as a mistake; since no code relies upon it, it may as well be fixed at any convenient opportunity.
Jan 27, 2015 at 7:45 AM
Glad to hear that you're starting to work on C# 7. Does that mean that you'll also start posting language design notes again? That's always been an interesting read, so it is unfortunate that you haven't published those notes recently.

If you're considering delegate and enum constraints, please also consider adding a constraint that allows you to get a pointer to a generic parameter. That would occasionally be useful when doing low-level programming (in my case: graphics). And: Don't forget about the pattern matching proposal, it would be awesome to have that in C# 7!
Coordinator
Jan 27, 2015 at 4:42 PM
Yes we've moved to posting language notes on github:
https://github.com/dotnet/roslyn/labels/Area-Design%20Notes

(so far there's only one from finishing up VB14, but we'll start C#7 notes within a few days).

I do want delegate and enum constraints to be one of the proposals with which we kick off C#7 design. That's why I was hoping we could start with a sponsor from the community who's already written up a detailed proposal. If not, I guess it'll fall to me, and I won't have time to write it up for a month or two.

But yes we'll start right away with Neal's pattern-matching proposal too.
Jan 27, 2015 at 4:55 PM
lwischik wrote:
Yes we've moved to posting language notes on github:
https://github.com/dotnet/roslyn/labels/Area-Design%20Notes

(so far there's only one from finishing up VB14, but we'll start C#7 notes within a few days).

I do want delegate and enum constraints to be one of the proposals with which we kick off C#7 design. That's why I was hoping we could start with a sponsor from the community who's already written up a detailed proposal. If not, I guess it'll fall to me, and I won't have time to write it up for a month or two.

But yes we'll start right away with Neal's pattern-matching proposal too.
What do we need to do with the proposal on this thread in order to bring it to a level that should work for these design meetings? I know that some of the discussions in the thread would need to be incorporated and that would include backing off of some of the additional features, particularly with enum.

I don't have a lot of time to dedicate to this but I would love to see it make it into the rounds early enough for consideration.
Coordinator
Jan 27, 2015 at 5:24 PM
Halo_Four wrote:
What do we need to do with the proposal on this thread in order to bring it to a level that should work for these design meetings? I know that some of the discussions in the thread would need to be incorporated and that would include backing off of some of the additional features, particularly with enum.
(1) Gather use-cases. At a minimum, EVERY use-case in this thread (even the ones that won't work with the proposal). Hopefully other real-world use-cases as well. Use-cases are the single most important part of the entire language design process. If the language designers believe the use-cases, and see evidence that they're common enough in the real world, then the something will happen!

(2) Figure out the straw-man proposal you want to make. It'll probably be changed around through the design process but we need to start somewhere.

(3) Write down those use-cases now, describing how they would or wouldn't work with your proposal. It's also really important to see callers of methods, not just the method declarations+bodies themselves, since there'll be a lot more lines of C# written which call these methods than declare them.

(4) Write down your proposal now. The best way to write a language design proposal is a simple example (already covered by the use-cases), then the language syntax, then the type-checking rules ("static semantics"), then the runtime behavior ("dynamic/operational semantics")

(5) Incorporate all of the objections from this thread somewhere, and rebuttals or "undecided" for each of them. We try to throw every objection at proposal to if any of them stick. The more objections we've seen listed, the more confident we are that we've covered every possible objection.

(6) Write this down in a markdown document. Once we've got our dotnet/roslyn github directory structure set up, you can submit it as a pull-request.


I think the biggest hurdle is making sure that we've gathered enough use-cases and objections to be confident. That's why it's so nice to do language design in the open and see these things before we start implementing the feature :)
Feb 14, 2015 at 7:04 AM
I just encountered a real world use case:

My generic class uses the type that I want to constrain to enum in a lambda expression that is passed to a 3rd party library as an expression tree. The code in that library that parses the expression tree expects the type to be an enum and generates a runtime error if it isn't. With the proposed constraint I could ensure that check happens at compile time rather than runtime.