Runtime: Availability of nullable attributes outside of Core 3.0

Created on 5 Aug 2019  Â·  23Comments  Â·  Source: dotnet/runtime

First issue here be kind :)

Following on from https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-15.md, we now have a way to annotate our existing APIs for nullable reference types with https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs (mirrored from CoreLib sources).

It took some digging to figure out what to do for a Core 2.1 project . I think the https://github.com/dotnet/docs/blob/master/docs/csharp/nullable-attributes.md assumes the reader is using Core 3.0. Is this only going into Core 3.0?

If so, would it be worth making it available in a NuGet to provide a complete nullable reference type experience in other framework versions, rather than leaving it to developers to implement their own?

area-Meta question

Most helpful comment

But once you set LangVersion yourself, there's nothing getting in your way provided that you have the appropriate types for the compiler, and that you don't use Default Interface Members.

Now if we could just shift a little towards a list of officially supported C# 8.0 features outside of netcoreapp3.0 or netstandard2.1, with features being blocked where the target does not support them, we've made a whole bunch of people very happy :)

All 23 comments

cc: @cartermp

Currently there will not be any nullable-aware APIs from CoreFX unless you are targeting netcoreapp3.0. We considered what it would mean to create a "nullable targeting pack" for netstandard2.1, but felt that it might be a bit too janky to jam that into NuGet alongside the .NET Core 3.0 release. Because we're not annotating all APIs for the .NET Core 3.0 release (only about ~20% done!) and existing annotations may be changed based on feedback, maintaining two sets of annotations during this churn would be difficult and error-prone. When things stabilize we'll likely revisit this point.

Thanks @cartermp this makes sense.

Just need the lovely @MadsTorgersen to confirm or elucidate the following now then :)

using C# 8.0 is only supported on platforms that implement .NET Standard 2.1 devblogs

Because against that, it doesn't feel right being able to use nullable reference types outside of Core 3.0. Particularly weird given it's a little incomplete on the annotations front - I had hoped it was the one feature we'd see in 4.8 too.

.NET Standard 2.1 includes the nullable annotation attributes1 and thus supports C# 8. Right now, the only implementation of .NET Standard 2.1 is .NET Core 3.0, but the intention is that Xamarin/Mono/Unity will support that in their upcoming releases.

Does this clarify the confusion of .NET Standard 2.1 vs. .NET Core 3.0?

1However, .NET Standard itself isn't annotated yet -- we'll do this once the rest of CoreFX is fully annotated and we feel we got them right.

Sorry if I’m making no sense Immo. I don’t think there’s any confusion with .NET Standard 2.1 vs. .NET Core 3.0 :)

I’ve focused on nullables because it’s the feature I’ve played with the most and presents an interesting scenario where we’d only see a partial implementation outside of Standard 2.1, requiring the developer to roll their own annotations. But then that depends on whether we see any C# 8.0 features outside Standard 2.1.

Right now, I can roll a .NET Core 2.1 application in VS 2019, switch to <LangVersion>8.0</LangVersion> and use the nullable ref type feature. If this remains possible in September then I’d be missing the annotations out-of-the-box. That I can do this at all, doesn’t fit with the above quote from Mads. Or am I just being daft?

Maybe this thread belongs in the csharplang repo now. Ultimately, my confusion (echoed by @kingboyk https://stackoverflow.com/a/57020770) is on where we will see C# 8.0. I’m tasked with supporting my colleagues in adopting C# 8.0 but we also maintain and actively develop many existing Core 2.1 (and netfx) applications where we’d love to hone our C# 8 skills. Yet I can’t even say with absolute confidence whether they’ll see any of the C# 8.0 features outside Standard 2.1 or not.

C# 8 will be fully supported on .net core 3.0 and .net standard 2.1 only.

If you manually edit the project file to use C# 8 with .net core 2.1, you are in unsupported territory. Some C# 8 features will happen to work well, some C# 8 features will work not too well (e.g. poor performance), some C# 8 features will work with extra hacks, and some C# 8 features will not work at all. Very complex to explain. We do not actively block it so the expert users who can navigate through it can do so. I would not recommend this unsupported mix&match to be used broadly.

From an enterprise viewpoint this is the confirmation I really didn't want to hear.

Nonetheless super clear @jkotas and good to have some certainty. I have no more questions :)

Thank your patience with me folk.

Sounds good :-)

Note that C# 8.0 is not meant for older targets, such as .NET Core 2.x or .NET Framework 4.x

FWIW I have a library that targets netstandard2.0 (and recently added netstandard2.1) and I annotate all my library by conditionally defining internal attributes for NotNull etc. for the netstandard2.0 target. And it works great in consuming projects even if they target these lesser platforms, so long as we're using the C# 8 compiler.

So is it strong guidance to avoid the C# 8 compiler unless folks target .NET Core 3.0? Or is it enough to use C# 8 and know to avoid the .NET Core 3.0-only features?
Is it documented what happens if a .NET Core 3.0 feature is used with C# 8 in a project that targets something lesser?

So is it strong guidance to _avoid_ the C# 8 compiler unless folks target .NET Core 3.0?

No. However, there are caveats. People like you who are willing understand -- and work around them -- are free to use C#. The point is, not all language features will work on down-level targets.

Is it documented what happens if a .NET Core 3.0 feature is used with C# 8 in a project that targets something lesser?

It will just work. The boundary of projects is described via their ABI and that's a function of the compatibility requirements we ensure will hold up. That's also why we model runtime features the compiler can depend on in code-gen as an API (RuntimeFeatures).

I don't think guidance is something we've considered here, though I suppose that's already been done in tools. We only default someone to C# 8 if they're targeting netcoreapp3.0 or netstandard2.1, and the vast majority of people just use the default. But once you set LangVersion yourself, there's nothing getting in your way provided that you have the appropriate types for the compiler, and that you don't use Default Interface Members.

But once you set LangVersion yourself, there's nothing getting in your way provided that you have the appropriate types for the compiler, and that you don't use Default Interface Members.

Now if we could just shift a little towards a list of officially supported C# 8.0 features outside of netcoreapp3.0 or netstandard2.1, with features being blocked where the target does not support them, we've made a whole bunch of people very happy :)

Unfortunately, our feedback this year has been exactly the opposite. People just get confused, since a language version being fully supported and being a default for a project with a given TFM generally means that everything works in that context. But that isn't the case. Attempts to explain this to people resulted in more confusion, leading to the decision as it exists today.

To expand a bit of what @cartermp siad: we don't support individual language features, we support the set.

Ready to admit I am a bit late to the show on this with the release imminent and I'm sure you've already debated it thoroughly.

we don't support individual language features, we support the set.

...and use NuGet packages to broaden the range of supported runtime versions for the set? :)

Not at all questioning the validity of the feedback received but perhaps some confusion could actually be avoided by following this familiar model from C# 7. Rigidly coupling language evolution to runtime also feels like a departure from what we've become accustomed to.

Perhaps I mix in strange circles but developers I hear from expected this model and even now that they know different, they still believe it will eventually happen, which is just as confusing? Probably not helped by the freedom to change LangVersion manually. I don't think I'm an absolute fool, yet despite what had been shared to date, I was still confused enough to post here :)

I think that unfortunately, it will slow the rate of uptake for the new features, which can only be a negative thing in the .NET community with less activity around them.

Maybe the compiler could define two sets of features then. Call one "8.0" and another "7.1" perhaps. Put all the features that work on downlevel runtimes in 7.1, and reserve 8.0+ for .NET Core 3.0+.

I'm having a great time using NRTs with .NET Framework and .NET Standard. If you use @sharwell's https://github.com/tunnelvisionlabs/ReferenceAssemblyAnnotator, the .NET Framework/Standard reference assemblies light up with the nullability annotations from .NET Core 3.0 APIs that match.

If you manually edit the project file to use C# 8 with .net core 2.1, you are in unsupported territory. Some C# 8 features will happen to work well, some C# 8 features will work not too well (e.g. poor performance), some C# 8 features will work with extra hacks, and some C# 8 features will not work at all. Very complex to explain. We do not actively block it so the expert users who can navigate through it can do so. I would not recommend this unsupported mix&match to be used broadly.

As I understand it (and @danielmarbach performed a test confirming it), that if I have, say, a netstandard2.0 project with <LangVersion>latest</LangVersion>, then the SDK/compiler will interpret that as C# 8.0. Given that this is "unsupported territory" where all kinds of weird things can happen, shouldn't the compiler at least emit a warning telling us so, rather than silently creating (potentially) garbage assemblies?

<LangVersion>latest</LangVersion>

From my perspective, this line means "break builds if/when possible". I strongly encourage users to put a specific version number in, otherwise each user will have a different experience and the burden of differences will fall entirely on the project maintainers. The option remains for compatibility purposes, but really should never be used.

@sharwell I agree with you, but I think it only took on the meaning "break builds if/when possible" with the release of C# 8.0. Or at least, the likelihood become far greater. Regardless, I believe the compiler should emit a warning when the language version being used is not supported by the TFM.

@adamralph Using that isn't any different from _not_ specifying a LangVersion, which has always meant that you could break builds across environments where you're not using the same compiler. So C# 8 isn't any different here, since any feature that's unrecognized by an arbitrary older compiler will result in a build break.

As for emitting a warning, I think we're currently not planning that for C# 8, but I wouldn't necessarily rule it out for C# 9. The problem is that individual features only check for type shapes and not an applicable lowest TFM. So it'd likely be more of a tooling diagnostic, since tooling hosts a compiler and is aware of TFMs (though this information is _not_ flowed through to Roslyn at the moment).

Phil doesn't this page say something else? https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version#defaultsNot specifying langversion means the defaults apply based on target framework 

Sorry, yes that’s correct. But the point remains; doing that in the past had the same effect

On Sat, Oct 26, 2019, at 12:43, Daniel Marbach wrote:

Phil doesn't this page say something else? https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version#defaultsNot specifying langversion means the defaults apply based on target framework
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/dotnet/corefx/issues/40039?email_source=notifications&email_token=ABQEJTQ7EEZBRDHHPGB6UZTQQSMWVA5CNFSM4IJONLMKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOECKPONQ#issuecomment-546633526, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABQEJTVP3B76XTHBKPOB3Y3QQSMWVANCNFSM4IJONLMA.

Was this page helpful?
0 / 5 - 0 ratings