-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add missing generic constraints #101504
Add missing generic constraints #101504
Conversation
Note regarding the
|
Added When you commit this breaking change:
Tagging @dotnet/compat for awareness of the breaking change. |
@@ -402,7 +402,7 @@ public abstract class Instrument<T> : Instrument where T : struct | |||
public ReadOnlySpan<System.Collections.Generic.KeyValuePair<string, object?>> Tags { get { throw null; } } | |||
public T Value { get { throw null; } } | |||
} | |||
public delegate void MeasurementCallback<T>(Instrument instrument, T measurement, ReadOnlySpan<System.Collections.Generic.KeyValuePair<string, object?>> tags, object? state); | |||
public delegate void MeasurementCallback<T>(Instrument instrument, T measurement, ReadOnlySpan<System.Collections.Generic.KeyValuePair<string, object?>> tags, object? state) where T : struct; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are all of these internal/non-public types?
Adding a constraint is a binary breaking, so just wanting to double check. -- notably, changing struct
to unmanaged
is binary compatible, but may not be source compatible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks to me like they're all impacting the ref assembly, so users calling these methods from unconstrained or less constrained APIs will break on roll forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are all public types. I've marked this change breaking. The constraints were already present in the runtime so I believe folks would have already observed runtime errors if they were violating the constraint.
System.TypeLoadException: GenericArguments[0], 'System.String', on 'System.Diagnostics.Metrics.MeasurementCallback`1[T]' violates the constraint of type parameter 'T'.
at Program.<Main>$(String[] args)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
If we don't want to add the source breaking for any of these we should remove the runtime constraint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constraints were already present in the runtime so I believe folks would have already observed runtime errors if they were violating the constraint.
👍. I was definitely more concerned about binary breaks, I think source breaks are fine particularly given that a TypeLoadException was already occurring.
@@ -2492,7 +2492,7 @@ public abstract partial class Enum : System.ValueType, System.IComparable, Syste | |||
public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("EnumFormat")] string? format) { throw null; } | |||
[System.ObsoleteAttribute("The provider argument is not used. Use ToString(String) instead.")] | |||
public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("EnumFormat")] string? format, System.IFormatProvider? provider) { throw null; } | |||
public static bool TryFormat<TEnum>(TEnum value, System.Span<char> destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("EnumFormat")] System.ReadOnlySpan<char> format = default(System.ReadOnlySpan<char>)) where TEnum : struct { throw null; } | |||
public static bool TryFormat<TEnum>(TEnum value, System.Span<char> destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("EnumFormat")] System.ReadOnlySpan<char> format = default(System.ReadOnlySpan<char>)) where TEnum : struct, System.Enum { throw null; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be removed in the implementation instead. Notice that TryParse APIs on this type do not have the Enum constraint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stephentoub what do you think? You added the API with the constraint in 62f3eb2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with above this already fails at runtime already with:
System.Security.VerificationException: Method System.Enum.TryFormat: type argument 'System.DateTime' violates the constraint of type parameter 'TEnum'.
The unconstrained generic methods (like Parse and TryParse) fail with:
System.ArgumentException: Type provided must be an Enum. (Parameter 'TEnum')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We approved it with the constraint but I believe this was because it was proposed like that and it made sense to us. Not having the constraint seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll remove the constraints from both ref and source and ensure that this API checks the T and throws an argument exception then (unless @stephentoub mentions otherwise).
@@ -65,7 +65,7 @@ public sealed partial class ByteEqualityComparer : System.Collections.Generic.Eq | |||
public override int GetHashCode() { throw null; } | |||
public override int GetHashCode(byte b) { throw null; } | |||
} | |||
public sealed partial class EnumEqualityComparer<T> : System.Collections.Generic.EqualityComparer<T>, System.Runtime.Serialization.ISerializable where T : struct | |||
public sealed partial class EnumEqualityComparer<T> : System.Collections.Generic.EqualityComparer<T>, System.Runtime.Serialization.ISerializable where T : struct, System.Enum |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not exposed publicly in reference assembly.
@@ -362,17 +362,17 @@ public sealed partial class ComObject : System.Runtime.InteropServices.IDynamicI | |||
System.Runtime.InteropServices.Marshalling.VirtualMethodTableInfo System.Runtime.InteropServices.Marshalling.IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableInfoForKey(System.Type type) { throw null; } | |||
} | |||
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.Exception), System.Runtime.InteropServices.Marshalling.MarshalMode.UnmanagedToManagedOut, typeof(System.Runtime.InteropServices.Marshalling.ExceptionAsDefaultMarshaller<>))] | |||
public static partial class ExceptionAsDefaultMarshaller<T> where T : struct | |||
public static partial class ExceptionAsDefaultMarshaller<T> where T : unmanaged |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW this would not have produced a runtime exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jkoritzinsky let me know that for these we want the constraints here.
Any type that doesn't follow the
: unmanaged
constraints will have unexpected runtime behaviors
{ | ||
public static T ConvertToUnmanaged(System.Exception e) { throw null; } | ||
} | ||
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.Exception), System.Runtime.InteropServices.Marshalling.MarshalMode.UnmanagedToManagedOut, typeof(System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller<>))] | ||
public static partial class ExceptionAsHResultMarshaller<T> where T : struct | ||
public static partial class ExceptionAsHResultMarshaller<T> where T : unmanaged, System.Numerics.INumber<T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently fails at runtime with
System.TypeLoadException: GenericArguments[0], 'MyStruct', on 'System.Runtime.InteropServices.Marshalling.ExceptionAsHResultMarshaller`1[T]' violates the constraint of type parameter 'T'.
{ | ||
public static T ConvertToUnmanaged(System.Exception e) { throw null; } | ||
} | ||
[System.Runtime.InteropServices.Marshalling.CustomMarshallerAttribute(typeof(System.Exception), System.Runtime.InteropServices.Marshalling.MarshalMode.UnmanagedToManagedOut, typeof(System.Runtime.InteropServices.Marshalling.ExceptionAsNaNMarshaller<>))] | ||
public static partial class ExceptionAsNaNMarshaller<T> where T : struct | ||
public static partial class ExceptionAsNaNMarshaller<T> where T : unmanaged, System.Numerics.IFloatingPointIeee754<T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently fails at runtime with:
System.TypeLoadException: GenericArguments[0], 'MyStruct', on 'System.Runtime.InteropServices.Marshalling.ExceptionAsNaNMarshaller`1[T]'
You might want to add the APICompat update from #101378 into this PR. |
Oh interesting. I wasn't aware that had already flowed. Maybe I'll just collapse this into the darc PR to reduce the churn, once I get consensus on the plan. |
Closing this in favor of fixing the ingestion PR where these errors are already popping. That lets me also checking the suppressions which I had to comment out here. |
In dotnet/sdk#40230 I added generic constraint checking to APICompat.
This fixes the places in runtime that were missing those. cc @jkotas @terrajobst
Note the suppressions in comparison to last release are commented out until we have an APICompat that validates the new rule in runtime.