Open timcassell opened 1 month ago
"Removing readonly
from a field" would then become a breaking change, if the field is static.
"Removing
readonly
from a field" would then become a breaking change, if the field is static.
Hm. Personally, I would be ok with that. But I understand how that would be troublesome for the ecosystem. What about annotating a static readonly field such that it will be considered a runtime constant, and removing that annotation will be a breaking change instead (it would be invalid to have the annotation on a non-readonly field)? A similar annotation could be used for the functions/properties as well.
Or perhaps it could be limited to static fields that are not accessible from outside the assembly; thus internal
, private
, or private protected
. That way, removing readonly
could not break third-party assemblies. Having to define internal static readonly IntPtrSize = IntPtr.Size;
just for use in attributes would be a bit annoying though.
Then, I wonder about reflection APIs. For System.Reflection.CustomAttributeTypedArgument, it might suffice if the object? Value
property just returned the runtime-constant value without indicating how it was obtained. This would not work with Assembly.ReflectionOnlyLoad (because the initialisation of the static field could not be executed) but .NET Core doesn't support that anyway. For System.Reflection.Metadata.CustomAttributeTypedArgument\<TType> though, I think there should be a way to get the expression or at least the metadata token of the field.
Actually, that might be just fine for the runtime to only support internal static readonly fields. The C# compiler doesn't have to have the same restriction, and could synthesize the field for you.
Or perhaps it could be limited to static fields that are not accessible from outside the assembly; thus internal, private, or private protected.
IMO it's not a good idea to add new meanings (or implications) to internal
and others.
The C# compiler doesn't have to have the same restriction, and could synthesize the field for you.
This doesn't solve the breaking-change problem. The compiler may try to synthesize an internal static readonly
from a public static readonly
in another assembly. What if that readonly
is removed? This will still be a valid program, but may give unpredictable results.
The C# compiler doesn't have to have the same restriction, and could synthesize the field for you.
This doesn't solve the breaking-change problem. The compiler may try to synthesize an
internal static readonly
from apublic static readonly
in another assembly. What if thatreadonly
is removed? This will still be a valid program, but may give unpredictable results.
Not having the same restriction doesn't mean the compiler must have no restrictions. It could have the requirement that used fields and properties be annotated with [RuntimeConstant]
, for example.
Also, even if that were to happen, I don't think it's a big deal. It would still be a runtime constant, evaluated only once.
Having [RuntimeConstant]
is a much better choice IMO.
The point is not to allow arbitrary existing static readonly
fields to be used in attributes (although they are treated as runtime constants by the runtime). This should be an opt-in feature. Only in this way the breaking-change problem can be solved.
In this case, is it possible to reuse the const
keyword in C# to allow any type, effectively making them [RuntimeConstant] static readonly
? This follows the recent trend of extending or redefining existing keywords for broader scenarios.
Currently, attributes only allow compile-time constants as constructor arguments/properties. If we were allowed to use runtime constants, attributes would become much more powerful.
What is considered a runtime constant? Existing consts, obviously, but also any
static readonly
field (which the tier-1 JIT treats as consts), as well as static pure functions and properties (e.g.IntPtr.Size
), and constant expressions (e.g. `IntPtr.Size 2`).This would enable things like lambdas in attributes (https://github.com/dotnet/csharplang/discussions/343), improved explicit layouts (https://github.com/dotnet/csharplang/discussions/4652), enabling users to do things like
https://github.com/dotnet/runtime/blob/7b1e788c34ee9a44f6dc548c170c153dd7eab559/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs#L339-L344
and more.
We don't currently have a way to declare a pure function. There is an old csharplang discussion about it (https://github.com/dotnet/csharplang/discussions/776), probably it's a new thing that would require runtime validation of purity.