-
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
[API Proposal]: System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute #66167
Comments
Tagging subscribers to this area: @dotnet/area-system-runtime-compilerservices Issue DetailsBackground and motivationWe've had a few times now in C# language design where the traditional method of requiring that a compiler understand a feature, a
For these cases, our strategy so far has been to use API Proposalnamespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public sealed class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
public string FeatureName { get; }
public string Language { get; init; }
}
} API Usageref struct S {} will be emitted as [CompilerFeatureRequired("ref-struct")]
struct S {} Alternative DesignsWe might also consider putting a list of features into the runtime, but that runs the risk of the runtime effectively becoming an arbiter of language features: if some third-party .NET language wanted to add their own strings, what would the process for that be? RisksNo response
|
/cc @stephentoub |
What's the plan to ensure that new/existing .NET languages are aware of this attribute and the requirements around understanding/respecting it? What about older compilers (say the C# native compiler) that have no awareness of this attribute? IIRC, that's one reason why |
Looks great! You might want to call out the fact that this only blocks the feature back to the version of the language that first knows about this attribute; e.g. C# 11. So we might need to allow some time to pass before we rely solely on this rather than |
The compiler could, I guess, add both |
We will work with our partners to make sure that they are aware, and I plan to reach out to third parties I know of (such as peachpie/cobol.net).
We will continue to be using both
Right, and in LDM we acknowledged that, and also acknowledged that we should have done this at that time as well. Better late than never 🙂 |
maybe a dumb question: is it possible to make old compilers ignore or disallow usage of declarations which have this attribute by putting a modreq on the constructor of |
|
Exactly the same as with
I did not have a specific scenario in mind, I simply wanted to make this option as flexible as
Not AFAIK. The general pattern in the BCL (that I know of) is to not used default parameters for attributes, and instead use properties. I don't see a particular issue with doing this.
Sure, I just wanted to point out the potential risk of putting such things in the BCL. |
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public sealed class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
public string FeatureName { get; }
public string? Language { get; init; }
}
} |
Up to the implementation. If we don't think the optionality is relevant, we can strip it from the proposal. We could also change it to a boolean |
I've marked this blocking so we get to it soon. I'm getting to the point with required members that I will need a finalized design for this attribute. |
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public sealed class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
public string FeatureName { get; }
public bool IsOptional { get; init; }
public const string RefStructs = nameof(RefStructs);
public const string RequiredMembers = nameof(RequiredMembers);
}
} |
CompilerFeatureRequired with an IsOptional property seems like a strange combination of names. |
This was brought up and discussed. We didn't feel strongly enough to address this because in order to make this clean, we'd basically need three types: namespace System.Runtime.CompilerServices;
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public sealed class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute(string featureName);
public string FeatureName { get; }
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public sealed class CompilerFeatureOptionalAttribute : Attribute
{
public CompilerFeatureOptionalAttribute(string featureName);
public string FeatureName { get; }
}
public static class CompilerFeatures
{
public const string RefStructs = nameof(RefStructs);
public const string RequiredMembers = nameof(RequiredMembers);
} Considering that these annotations aren't written nor read by humans, it felt like over engineering. |
Would it help if the type was renamed to something like |
Background and motivation
We've had a few times now in C# language design where the traditional method of requiring that a compiler understand a feature, a
modreq
, has been insufficient in one of a couple of ways:modreq
s to be applied, such as a type declaration. This is the case for ref structs.modreq
s affect binary compatibility isn't desired. For example, the required members feature doesn't want to mark constructors with amodreq
because that would break binary compat if all required members were later removed from a type, as themodreq
is part of the signature.For these cases, our strategy so far has been to use
ObsoleteAttribute
with a specific error message the compiler knows to ignore. This is not a great solution, however. If the user puts an obsolete attribute of their own set to warning, it overrides ours. Obsolete methods and types are also perfectly fine to use from an obsolete context. To address these deficiencies going forward, we'd like to add a new tool to our toolbox, a poison attribute of sorts, that allows the compiler to mark a type or other symbol that supports attributes as requiring an understanding of a specific feature. For equal flexibility withmodopt
vsmodreq
, I've also added an ability to say that this feature only applies to a single language, if that language wants to allow other languages free access, but prevent older versions of its own compiler from using the API without correct understanding.The semantics of this attribute are:
Language
is null, any compiler that does not understandFeatureName
is required to disallow usage of the member attributed with it.Language
is not null, any compiler for that language that does not understandFeatureName
is required to disallow usage of the member attributed with it. Compilers for other languages are free to ignore the attribute.API Proposal
API Usage
will be emitted as
Alternative Designs
We might also consider putting a list of features into the runtime, but that runs the risk of the runtime effectively becoming an arbiter of language features: if some third-party .NET language wanted to add their own strings, what would the process for that be?
Risks
It is important to note that this enforcement mechanism will only work going forward. Older compilers are going to have no idea that this is a thing and not block access. That being said, if we don't add something like this now, we'll never get to a point where we can solely rely on this attribute as the enforcement mechanism.
The text was updated successfully, but these errors were encountered: