-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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 helper method to describe features supported by the runtime #20623
Comments
CC @marek-safar to represent mono. |
This is useful but I am curious where do you think this API will live or whether it'll always be compiled in to every assembly |
I think this API should live in core libraries (ie. along with the |
The API needs to live in corlib: the assembly with no external references and containing The enum describes runtime features hence needs to be in corlib as it is tightly coupled with a given runtime. It's the only assembly that can correctly describe runtime capabilities. |
That's little complication as there is no single corlib anymore (think of netstandard(s)) but I guess runtime does not really need to read this enum type declaration after all. |
The runtime doesn't need to read this enum. But the compiler needs to know the capabilities of a runtime to decide what features it can enable. This problem exists precisely because there are so many runtimes out there now 😦 Consider default interface methods as an example. When targeting Desktop 4.6 this feature can't be enabled. C# though has no notion of TFMs, frameworks, etc ... All the compiler sees is references. The only library we can use to reliably determine runtime features is corlib, hence this is where the type needs to be. |
cc @terrajobst |
As an alternative to an enum I would like to suggest to use a static class with Boolean constant fields instead. So, something like: namespace System.Runtime.CompilerServices
{
public static class RuntimeFeatures
{
public const bool FixedGenericAttributes = true;
public const bool SupportsDefaultImplementation = false;
// ... this list would grow over time
}
} This way we will be able to rely on the value of a particular field vs. just its presence or absence. It will be much easier to right conditional code based on the fact that a certain feature is supported. For example: if (RuntimeFeatures.TheFeatureIAmInterestedIn)
{
// Take advantage of the feature
}
else
{
// The feature is not supported, perform an appropriate recovery.
} |
From discussion with Aleksey, we came up with a design that addresses both compile-time and runtime-time scenarios reasonably well. namespace System.Runtime.CompilerServices
{
public static class Runtime
{
[Supports(
RuntimeFeatures.FixedGenericAttributes,
RuntimeFeatures.SupportsDefaultImplementation)]
bool Supports(RuntimeFeatures value)
{
switch (value) { ... } // this implementation needs to match the declaration in attribute above
}
}
public class SupportsAttribute : Attribute
{
public SupportsAttribute(params RuntimeFeatures[] supported) { ... }
}
} Checks at compile-time would inspect the |
|
It doesn't. Net Standard describes the set of APIs that are available to a program. This particular enum is describing the set of runtime features available to compilers. Concrete example: there is a PR out to add support for generic attributes. This isn't about API surface but rather enabling a particular scenario at the CLR level. Not all runtimes support this feature. Hence in order to know whether or not to allow the feature the compiler needs to understand if the runtime is going to support it.
The compiler is mostly just cracking the metadata and looking for the existence of features that is is aware of. |
So if I write a library that targets .Net Standard x.y, I will never be able to use generic attributes? Because the compiler doesn't know what runtime it will run on, only that I target that specific version of .Net Standard.
Yes, but the latest proposal by @jcouv implied that there are run-time scenarios as well. I was specifically asking about those. |
This will be true for Net Standard 2.0. It's possible in the future Net Standard will begin requiring that runtimes support generic attributes and it will be included in the definition of corlib. |
I like the idea to use APIs to describe the runtime capabilities so that we can model which features a runtime implementing a specific .NET Standard version has to support. This allows compilers to rely on that. The existence of an attribute application is too subtle for my taste: it's easily missed by regular diffing tools and not covered by our existing API compliance tools. We could of course fix this, but I'd rather see an actual API delta (missing type or missing field). Also, what's the value of dynamic checking? What code would use that and how would that work? Tagging @migueldeicaza, as he seemed to have concerns as well. |
If you want to do a light up in a library. There are really two distinct cases to think about:
|
A related area is Vectors where |
I could see how feature employing dynamic codegen (like dlr) could benefit from dynamic checks for jit bugs and such. As a general light-up - not sure. The alternative code paths would still need to be compiled even if not used. |
I can not pinpoint exactly the problem, but the proposal generally leaves a bad taste in my mouth. We seem to be mixing up two issues here: how should the compiler choose what code it generates, and later in the discussion we added a discussion on how this might be potentially useful at runtime. The former problem seems to be, how should the compiler decide how to generate code. It seems that we have made a leap from the requirement to the implementation specific detail that is to embed into mscorlib a fingerprint that describes the capabilities, and then rely on this as the mechanism to control the code generation. This is new territory in the compiler world. Other languages rely on compile-time options to drive the compilation process, and it is a process that is well understood with well-understood side effects. I do not have a complete grasp of what the side effects of these choices will be right now. The second capability, the runtime capability to probe for features is a very useful one, but one that I do not believe should rely on enumeration values. I would like to have something like:
We would have to standardize the capability names, but it would allow code that is compiled with an older toolchain to lightup with new toolchains. The string return can be used either for a quick bool on/off, or can be used to return a capability-specific payload:
|
We've discussed this today between us and the compiler guys. Here is what we left with: namespace System.Runtime.CompilerServices
{
public static class RuntimeFeature
{
// Presence of the field indicates runtime support
public static readonly string FixedGenericAttributes = nameof(FixedGenericAttributes);
public static readonly string DefaultImplementation = nameof(DefaultImplementation);
// Runtime check
public static bool IsSupported(string feature);
}
}
// Usage
if (RuntimeFeature.IsSupported("SomeNewerFeature")
{
// Use feature when emitting code at runtime (CodeDOM, RefEmit etc)
}
@jcouv, could update your proposal above to reflect this? |
@terrajobst Updated proposal description to match the agreement you documented. Thanks |
Do I understand it correctly that namespace System.Runtime.CompilerServices
{
public static class RuntimeFeature
{
public static bool IsSupported(string);
}
} will exist in netstandard.dll API and when executed the runtime specific mscorlib will handle the true/false logic. |
The API approval is for .NET Core only. We will update .NET Standard in future version when all/most platforms support the API. All platforms that can support it should do it. |
Since .NET Standard 2.0 can only model existing APIs it will not have RuntimeFeature in 2.0. I expect this type and some of the runtime features to become available with .NET Standard 2.1. |
Just thought of a runtime capability, that would be convenient to check for - ability to unload types/assemblies. I have seen many examples where code goes to great lengths trying to not retain runtime type objects - do not put types as keys (unless the type is a known primitive) in Dictionaries/Caches, do not cache delegates .... - That is with the assumption that retaining type objects will prevent assembly unloading. However, if I am not mistaken, only a few, if any, runtimes can actually unload types/assemblies. It would be helpful for the dynamic scenarios, to know if unloading is not possible and thus no need to worry about, for example, using type objects as keys in caches. This is basically an ask for a capability string like public static readonly string CollectibleAssemblies = nameof(CollectibleAssemblies); Any better name is more than welcome. |
Added the "CollectibleAssemblies" capability string as a separate API proposal |
Is there a particular reason why the switches are |
@MichalStrehovsky Yes, |
This does not make sense to me. Could you please explain in detail how const would defeat the runtime detection? |
It could defeat the runtime detection in const case if a value of the "feature" field/const is implementation detail (e.g each runtime can use its own value, for example shorter string for faster compares) but it's not clear from the proposal if that's the intention as it mixes readonly field and literal value together. |
The static fields cannot be used in combination with the runtime detection. If the runtime does not support the feature, it won't have the matching static field. |
I originally proposed them to be I don't believe this scenario is very sensible to defend against; in practice (for instance in the .NET Framework) most binaries compiled against any 4.x framework will run on an older runtime and only fail for cases where an API is being used that didn't exist before. Thus, apps that depend on new behavior might fail in less deterministic ways. This is usually not a problem because apps have For .NET Core, a similar thing holds as the app's So yes, we should change the fields to become |
Updated the description (up top) to use |
@jcouv @terrajobst I'll send another PR to update the comment with the proposed usage. |
@OmarTawfik I suspect this can be closed (coreCLR and mscorlib/desktop are done, and type-forwards were added). |
@jcouv I'll wait for nightly build to do one more verification. |
@jcouv @dotnet/roslyn-compiler This is now done and merged. |
@OmarTawfik can you please link PR # or commit #? |
@karelz |
Thanks! |
@marek-safar any information needed on my side for mono? |
@OmarTawfik What .net version will support this API? |
cc @tmat, who I think was also interested in this feature. |
@marek-safar core 2.0 and desktop 4.7.1. |
We are finishing implementation of Portable PDBs for Desktop FX 4.7.1 I'd like to add
to Desktop FX in the same check-in. Any objections? @noahfalk FYI |
@tmat can you expand on what support went into desktop for portable PDB? Hard to understand where this may get used based on the limited information in the comment. |
@jaredpar Currently there is no support for Portable PDBs in Desktop FX. We are finishing support in stack traces for portable and embedded PDBs for 4.7.1. Features that generate code at runtime (such as scripting) would benefit from being able to detect whether the runtime supports Portable PDBs or not, as they could emit Portable PDBs instead of Windows PDBs. Emitting Portable PDBs has perf benefits. |
@tmat, please start an new CoreFX API proposal (linked to this one). Every new feature the runtime declares will go through new API discussion/review. |
Do you need it in .NET Core 2.0? We should be forking into rel/2.0.0 branch today. |
@karelz It would be nice to have it there too. Not strictly necessary since there are other ways to detect Core CLR, but it would certainly simplify things. I can prepare PR in a few minutes. |
@tmat you need API review first. Then we can talk about PR. |
OK. |
Portable PDBs: https://github.com/dotnet/corefx/issues/19788 |
As we ramp the compiler and runtimes together, we want to prevent users from compiling code that we know will fail against a certain runtime.
For instance, we plan for newer runtimes to support default implementations in interface, and the compiler should be able to detect that you're compiling against an older runtime without that support.
Previously, this was done by having the compiler look for APIs that were added coincidentally with the new support. But as we expect to run into this problem more often, it makes sense to have a systematic design for testing feature support.
The main consumer of this API would be the compiler. Whenever it needs to check for runtime support, it would look for the corresponding well-known enum member (for instance,
System.Runtime.CompilerServices.RuntimeCapabilities.SupportsDefaultImplementation
). If the member exists, then the feature check is successful (the feature is supported). The value for that enum member is ignored.A number of feature requests or known bugs interfering with compiler or language evolution is documented in dotnet/csharplang#251
Proposed API
(Updated after API review 3/28/2017)
(Updated to use "const" instead of "static readonly" after further discussion 4/13/2017)
Static detection. The presence of a field alone indicates whether the code compiled against it can rely on the runtime providing the feature. This allows us to model runtime capabilities like any other API, which includes leveraging our existing infrastructure for detecting breaking changes. Also, this allows us to express this in .NET Standard and will version like any other API.
Runtime detection. There are some (very limited) scenarios where someone might want to do feature detection at runtime, in particular runtime code generation using RefEmit. We don't expect many developers to call this API. Usage of this API will always involve a literal, as having a field in scope would imply
IsSupported
must returntrue
. Developers using light-up would discover the strings by looking at the documentation of a particular .NET platform.Original proposed API
(rejected in favor of the one above)
Maintenance process
Adding new enum members would go through the standard API review process.
The main requirement is that enum names should mean the same thing across the different runtimes (desktop, core, RT, mono).
Also, there needs to be documented agreement between the runtimes and compilers about the meaning of each new member.
CC @gafter, @AlekseyTs, @VSadov, @jaredpar
The text was updated successfully, but these errors were encountered: