xamarin / xamarin-macios

.NET for iOS, Mac Catalyst, macOS, and tvOS provide open-source bindings of the Apple SDKs for use with .NET managed languages such as C#
Other
2.47k stars 512 forks source link

[Announcement] Breaking changes in .NET 6 for iOS, tvOS and macOS #13087

Closed rolfbjarne closed 2 years ago

rolfbjarne commented 3 years ago

In order to fully support the new native types in C# (nint, nuint), we’ve unfortunately realized that we can’t keep compatibility with existing code or assemblies built for Xamarin.iOS or Xamarin.Mac.

The consequences are as follows when upgrading a project to .NET 6:

  1. All code must be recompiled to support .NET 6. Existing assemblies (such as NuGets built for the old TargetFrameworkIdentifier xamarinios10) won’t work and won’t be supported. Non-xamarin specific assets for net4.x, netstandard, netcoreapp, net5.0+, etc. will work fine however.
  2. Existing code might need to be modified. We’ll post a more detailed document with examples of code changes that might be required at a later point.

In addition, we’re taking the opportunity to fix a few mistakes we’ve made in the past, that we’ve been unable able to address because they were breaking changes.

These are the main changes:

  1. Removal of the System.nint and System.nuint types. We’re going to use the nint and nuint types as defined by C# (they are System.IntPtr and System.UIntPtr under the hood).
  2. Removal of the System.nfloat type. We're going to use the System.Runtime.InteropServices.NFloat type instead. ~For the System.nfloat type we’re probably going to keep the type [1], but move it to a different namespace.~
  3. Use a different type than System.IntPtr for handles (the NSObject.Handle property): ObjCRuntime.NativeHandle. This is to avoid confusion between nint/nuint and handles.
  4. Move all the types in the OpenTK namespace elsewhere.

We also have a list of minor changes we’ve wanted to do for a long time: https://github.com/xamarin/xamarin-macios/issues/5107. We’ll be going through this list and implement the ones that make sense and that we have time to complete.

The first preview with these breaking changes will be Preview 11.

Reference: https://github.com/xamarin/xamarin-macios/issues/10508 Reference: https://github.com/dotnet/designs/pull/252 Reference: https://github.com/NuGet/Home/issues/11338

[1]: ~System.Runtime.InteropServices.NFloat is not a good replacement for System.nfloat, because it’s a very basic type intended only for interop scenarios. For instance, it does not have any operators, so code like “NFloat + NFloat” does not compile.~ The operators were implemented and back ported to .NET 6, so the concerns with System.Runtime.InteropServices.NFloat went away.

marek-safar commented 3 years ago

This also means that we should change precedence rules for net6-ios/tvos/macos TFMs as were designed in https://github.com/dotnet/designs/blob/main/accepted/2021/net6.0-tfms/net6.0-tfms.md.

/cc @mhutch

rolfbjarne commented 3 years ago

@marek-safar I have a PR for that: https://github.com/dotnet/designs/pull/252

mattleibow commented 3 years ago

If NFloat is missing things, maybe operators should be added to that? What other features need to be added to that to make it work?

Not sure if it is going to have a weird scenario next year where half the stuff is xamarin nfloat and the other is dotnet nfloat.

jkotas commented 3 years ago

Can nfloat be replace with double in the public surface to keep things simple? nfloat does not have much advantage anymore now that almost all Apple devices are 64-bit.

NogginBops commented 3 years ago

This is the time to finally fix the OpenTK issue! You are doing a binary breaking change here which was the only reason stated for why the OpenTK assembly types where not completely removed from Xamarin when this issue was raised before. This has been an issue for more than 6 years and it would be soooooo good to finally have it fixed. It's in the list from #5107 as Move OpenTK types out of platform assemblies.

Perksey commented 3 years ago

Thanks for this. To clarify for those unfamiliar with the Xamarin project planning/schedule, where on the .NET 6 shipping schedule does Xamarin "Preview 11" fall?

Perksey commented 3 years ago

@NogginBops I believe this is point 3 in the issue description?

rolfbjarne commented 3 years ago

@jkotas

Can nfloat be replace with double in the public surface to keep things simple? nfloat does not have much advantage anymore now that almost all Apple devices are 64-bit.

Yes, it's technically possible to expose double instead of nfloat, but there are still 32-bit iOS devices out there (which we support), and if we add (back) support for the Apple Watch in the future, we'll be running in 32-bit mode there too (there's no 64-bit Apple Watch).

Our feeling is that exposing double instead of nfloat would mean that we're not accurately mirroring the underlying platform, which can lead to unexpected behavior. One example would be if code set a double property on a 32-bit platform, and then read it back, you might get a different value back.

A more complex example would be a matrix of nfloats:

https://github.com/xamarin/xamarin-macios/blob/main/src/CoreGraphics/CGAffineTransform.cs#L39-L45

with numerous math operations defined on it, and where you might get different behavior whether we implement something in managed code:

https://github.com/xamarin/xamarin-macios/blob/0ede53d01baa4beefe69900fb470193e9f57b55e/src/CoreGraphics/CGAffineTransform.cs#L258-L262

or we P/Invoke into native code:

https://github.com/xamarin/xamarin-macios/blob/0ede53d01baa4beefe69900fb470193e9f57b55e/src/CoreGraphics/CGAffineTransform.cs#L272-L278

rolfbjarne commented 3 years ago

@mattleibow

If NFloat is missing things, maybe operators should be added to that? What other features need to be added to that to make it work?

NFloat was designed that way, so I feel it's unlikely to change: https://github.com/dotnet/runtime/issues/13788

Not sure if it is going to have a weird scenario next year where half the stuff is xamarin nfloat and the other is dotnet nfloat.

The fact that it was designed to only show up in the interop layer, and not public API, makes me hope that won't happen.

Additionally we're moving our nfloat type out of the System namespace to lessen the impact if it were to happen.

rolfbjarne commented 3 years ago

@NogginBops

This is the time to finally fix the OpenTK issue!

Correct, that's planned.

rolfbjarne commented 3 years ago

@Perksey

Thanks for this. To clarify for those unfamiliar with the Xamarin project planning/schedule, where on the .NET 6 shipping schedule does Xamarin "Preview 11" fall?

I can't give any specific dates, but Preview 10 will be released around the same time .NET 6 is launched (.NET Conf Nov 9th-11th), so it's the preview after that, and this fall we've been having a new preview every months or so.

Perksey commented 3 years ago

Ok cool. So I guess my last question is: will the net6.0-ios (and friends) TFM remain preview for a little while after .NET 6 GA?

cleardemon commented 3 years ago

Does this mean all the APIs are also being moved from usages of double (not even System.nfloat) to the new C# nfloat? Similarly for int types. I'd imagine there's quite a few overrides that need changing, if that's the case.

there's no 64-bit Apple Watch

Not strictly true, S4 and up are at least dual-core 64-bit ARM SoC (wiki). Series 3 is still 32-bit, though, and supported.

dalexsoto commented 3 years ago

Ok cool. So I guess my last question is: will the net6.0-ios (and friends) TFM remain preview for a little while after .NET 6 GA?

Correct

rolfbjarne commented 3 years ago

Does this mean all the APIs are also being moved from usages of double (not even System.nfloat) to the new C# nfloat?

No, we're not moving any double or float usages to any variety of nfloat [1] - note that there's no C# nfloat type (like there are C# nint and nuint types), there's only a System.Runtime.InteropServices.NFloat struct in .NET 6, which, as mentioned in the description, isn't a good replacement for our System.nfloat type.

Similarly for int types. I'd imagine there's quite a few overrides that need changing, if that's the case.

Yes, any code that currently uses System.nint and System.nuint will have to migrate to use the C# nint and nuint types.

However, if your code just uses nint and nuint, it should compile just fine without any changes.

there's no 64-bit Apple Watch

Not strictly true, S4 and up are at least dual-core 64-bit ARM SoC (wiki). Series 3 is still 32-bit, though, and supported.

Strictly speaking you're correct. It's a 64-bit CPU, but it runs in a weird 32-bit mode. Applications are basically 32-bit, and as such, we have to treat it as a 32-bit architecture.

[1]: unless it happened to be a mistake to not use nfloat in the first place, but that's quite rare, we've been able to fix most of such mistakes already

jkotas commented 3 years ago

Our feeling is that exposing double instead of nfloat would mean that we're not accurately mirroring the underlying platform, which can lead to unexpected behavior. One example would be if code set a double property on a 32-bit platform, and then read it back, you might get a different value back.

Yes, it is a trade-off. We typically opt for more native .NET look-and-feel than to precisely expose platform corner cases in .NET APIs. It is not unusual to see manual casts from double to nfloat in Xamarin code that is not a native .NET look-and-feel. Do you think it is the right trade-off to make people type these manual casts and keep learning about the special nfloat type going forward just to avoid hitting corner cases on watch? (I am fine with you saying that it is the right tradeoff, just making sure that we are asking the right questions.)

spouliot commented 3 years ago

In theory you could use System.Runtime.InteropServices.NFloat in the internal interop code and expose double in the public API. For 32bits support that would require the generator (or manual bindings) to add the extra cast (from float to double). It's likely more work today, but less work in the future (for the team). OTOH it would be easier for consumer from day 1.

WRT Handle you could try to changes NSObject to subclass SafeHandle (so only one class needs to be instantiated per native object). Dealing with non-NSObject INativeObject types (and related runtime assuptions) will be harder... but your move toward using NativeObject subclasses should help (and that could also subclass SafeHandle).

Anyway have fun breaking things :-)

Therzok commented 3 years ago

Move all the types in the OpenTK namespace elsewhere.

@rolfbjarne Does this mean that OpenTK will have its own registrar, possibly opening up the per-dll-registrar discussion?

If so, having a dll-per-framework model would be great. Currently, there is no way to have only Foundation.dll or AppKit.dll, which would define whether the library can be run as headless, considering the ObjCRuntime is initialized, enabling proper layering of an application on top of Xamarin.Mac.

rolfbjarne commented 3 years ago

@Therzok

Move all the types in the OpenTK namespace elsewhere.

@rolfbjarne Does this mean that OpenTK will have its own registrar, possibly opening up the per-dll-registrar discussion?

No, not in the short term. The per-dll-registrar feature is unrelated to this (and rather unlikely to be completed for a while due to time constraints).

If so, having a dll-per-framework model would be great. Currently, there is no way to have only Foundation.dll or AppKit.dll, which would define whether the library can be run as headless, considering the ObjCRuntime is initialized, enabling proper layering of an application on top of Xamarin.Mac.

A dll per framework is something we've discussed internally in the past, but there are circular references between Apple's frameworks, which makes the implementation rather non-trivial, and the cost was deemed not worth it (which doesn't mean that it can't be brought up again at some point).

Therzok commented 3 years ago

No, not in the short term. The per-dll-registrar feature is unrelated to this (and rather unlikely to be completed for a while due to time constraints).

:+1: I thought it was not going to be slotted for net6, but considered asking, since OpenTK being moved elsewhere means it has to receive static registrar support somehow.

A dll per framework is something we've discussed internally in the past, but there are circular references between Apple's frameworks, which makes the implementation rather non-trivial, and the cost was deemed not worth it (which doesn't mean that it can't be brought up again at some point).

Understood, should I file an issue for this? The split doesn't have to be per-framework, merged assemblies that need AppKit/UIKit and those that don't might be a good compromise.

rolfbjarne commented 3 years ago

@Therzok

A dll per framework is something we've discussed internally in the past, but there are circular references between Apple's frameworks, which makes the implementation rather non-trivial, and the cost was deemed not worth it (which doesn't mean that it can't be brought up again at some point).

Understood, should I file an issue for this? The split doesn't have to be per-framework, merged assemblies that need AppKit/UIKit and those that don't might be a good compromise.

Yes, that would be great.

commonsensesoftware commented 2 years ago

@rolfbjarne with respect to:

[1]: System.Runtime.InteropServices.NFloat is not a good replacement for System.nfloat, because it’s a very basic type intended only for interop scenarios. For instance, it does not have any operators, so code like “NFloat + NFloat” does not compile.

I completely understand and agree with this value judgement - today. Does that still make sense with the advent of Generic Math on the horizon? Granted, it is in preview at the moment, but if it were fully flushed out, it would seem pretty trivial to have NFloat implement all the necessary interfaces to make it a fully functional drop-in for nfloat by implementing it through to the underlying native implementation (e.g. Single or Double). There might be one or two other gaps, but it would seem those are addressable by way of extension methods or simply asking to have it added to NFloat.

Ultimately, it feels like NFloat and nfloat serve the same purpose, but have different features and capabilities. Wouldn't converging to one be preferable? NFloat would seem to be the long-term winner. I concede that nfloat might have to stay around for the short term. Backing nfloat with NFloat under the hood or, at least, making it convertible to and from NFloat may set up an easier transition in the future. As an example, have interop APIs only use NFloat, but expose nfloat where manipulation in managed code may be necessary. That also feels like it has parity with the approach to using SafeHandle.

"Why have one when you can have two at twice the cost?"

Perksey commented 2 years ago

I think IS.NFloat not implementing any arithmetic operators was by design (@tannergooding?), so even if generic math comes along it likely will not implement those interfaces. But I could be wrong, and if that is the case then yes IS.NFloat would theoretically be a suitable replacement given we're already down the rabbit hole of ABI breaks.

commonsensesoftware commented 2 years ago

@Perksey I get that it was by design - of NFloat anyway. Clearly, it exists for the purposes of providing interop, but nothing more. By name and description, NFloat and nfloat are meant to do the same thing, even they have different features. Adding stuff to a core library requires a lot of thought and consideration. Once you add it, it's pretty much there forever. To that end, it's not that hard to rationalize why NFloat is currently so feature incomplete for general usage. Creating a completely separate nfloat that does everything NFloat does and more seems wrong and is definitely confusing (to use).

While it may have been by design, the design of NFloat should be revisited. If I'm way off the mark and these two types do completely different things, then the documentation - at minimum - should call out those differences (aside from obvious feature differences). The existence and use nfloat validates there is a clear and strong use case for a fully-featured NFloat (IMO). There could very well be other scenarios where it could be useful in constrained systems such as some IoT applications.

Ultimately, this probably isn't the right forum to discuss the historical design of NFloat. If we agree that NFloat should be the correct replacement for nfloat, regardless of what its original design was, then this issue should be the catalyst to support changing/enhancing the design of NFloat to provide the necessary capabilities. A formal discussion in the runtime repo would establish a path forward that either sees nfloat get sunset once and for all or squash the conversation that NFloat should be enhanced. I don't know that I'm the best person to champion to do that, but I'm more than willing to lead the charge.

At least for me, I find the answer "Well, that's just how NFloat works and it's by design" extremely unsatisfactory. If we agree that NFloat should be the right thing, then I'm of the opinion we should push in that direction, even if it doesn't meet all the needs of today. The design of NFloat is what it is - for the moment, but it doesn't necessarily have to stay that way.

Perksey commented 2 years ago

While it may have been by design, the design of NFloat should be revisited.

For .NET 6 it's too late already, the API is already finalized. So in any case Xamarin will need their nfloat on .NET 6. And I suspect even if in .NET 7 NFloat gets matching features, Xamarin will want to keep their nfloat to avoid API differences between framework versions (as that would be extremely confusing).

EDIT: Unless GA mobile support is being delayed to .NET 7, in which case we can go with NFloat and push through the operators et al through API review, but that'll have to be a discussion to happen behind closed doors as I doubt "we're delaying to .NET 7" is the sort of thing MSFT would want to announce willy nilly!

tannergooding commented 2 years ago

Use a different type than System.IntPtr for handles (the NSObject.Handle property). Exactly which type(s) has not been decided yet. This is to avoid confusion between nint/nuint and handles.

@rolfbjarne, sorry for the late comment on this, I had missed the original issue.

I don't think this one is a good idea. The rest of .NET uses IntPtr to represent handles and will continue doing so moving forward. As it stands "right now", IntPtr and nint have slightly different semantics. Even if that were to change, I still don't think it would be problematic as there are already operators/methods exposed on IntPtr that you wouldn't want to use in the face of true opaque handles.

I think IS.NFloat not implementing any arithmetic operators was by design (@tannergooding?)

@Perksey, The reviewed design was indeed that it be purely an interchange type (this was partially influenced based on input from @jkotas, as my initial design did include basic operators, etc).

Given that nfloat is the "ABI primitive", if it having operators or not was the deciding factor of Xamarin using it or continuing to use their own custom version of nfloat, then we should probably revisit this. Adding the operators wouldn't be overly complex and would be "inline" with a lot of the "generic math" changes already being done for .NET 7.

If, however, Xamarin decided to use double in the public surface area and NFloat on the "backend", I think it would be fine to continue being purely an interchange type.

commonsensesoftware commented 2 years ago

@Perksey You are right and I agree. @tannergooding more or less confirmed what I suspected. Here's my thoughts as to how this would play out as it relates to the breaking changes and future evolution here.

Option 1

  1. Only use or publicly expose NFloat on API surfaces a. This assumes that NFloat could some day replace nfloat b. This would largely mitigate future breaking changes when/if NFloat becomes a formal replacement
  2. Keep nfloat for Xamarin because NFloat is not ready to completely replace it
  3. Refactor nfloat and/or provide extension methods to easily convert between NFloat and nfloat a. Implicit conversions should be a safe like-for-like, but I'd be just as happy with explicit conversions
  4. Some day (.NET 7, 8, 9...) nfloat is marked [Obsolete] and perhaps even removed

Using this approach would stabilize APIs themselves to NFloat, but provides easy-to-use conversions to nfloat when math operations are necessary.

Option 2

Having thought about it a little more, this would be my preferred and suggested approach:

  1. Only use or publicly expose NFloat on API surfaces
  2. Provide a math class (ex: MathNF like there's MathF) that provides all of the operations, functions, and constants currently exposed by nfloat. a. For example: public static NFloat Add(NFloat x, NFloat y) b. This type need not, and probably should not, live under the System namespace
  3. Some day (.NET 7, 8, 9...) the provided math class will no longer be necessary because NFloat provides intrinsic features that make it natural to use. a. While the math class is no longer necessary it's still fully valid and doesn't need to be necessarily deprecated or removed

A Math class doesn't represent the "ABI Primitive", it just provides the operations you'd do on the primitive. This path means you'd never have to worry about confusing NFloat with nfloat or having to deprecate/remove nfloat at some future point in time.

This approach is a bit less than desirable for how it would be used in the short-term, but that can be mitigated in a couple of ways:

User-Defined Extension Methods

If devs care enough, they can add extension methods to make things feel more natural. For example:

internal static class NFloatExtensions
{
    public static NFloat Add(this NFloat x, NFloat y) => MathNF.Add(x, y);
    // ... and so
}

Not quite x + y, but x.Add(y) is close.

Provide Scaffolding

A scaffolding template can be provided that would produce a substitute NFloat user-defined type that can be freely swapped out for IS.NFloat at some point in the future if a developer so chooses. This is purely only if a developer wants the usage to feel natural and is probably only convenient if you're doing a lot of NFloat math. Something like:

namespace MyProject;

using nfloat = System.Runtime.InteropServices.NFloat;

internal readonly struct NFloat : IEquatable<NFloat>
{
    private readonly nfloat value;
    public NFloat(nfloat value) => this.value = value;
    public static implicit operator nfloat(NFloat x) => x.value;
    public static implicit operator NFloat(nfloat x) => new(x);
    public static NFloat operator +(NFloat x, NFloat y) => new(MathNF.Add(x.value, y.value));
    public static NFloat operator -(NFloat x, NFloat y) => new(MathNF.Substract(x.value, y.value));
    // ... and so on
    public bool Equals(NFloat other) => value.Equals(other.value);
    public override bool Equals(object? obj) => obj is NFloat other && Equals(other);
    public override int GetHashCode() => value.GetHashCode();
    public override string ToString() => value.ToString();
}

With the exception of the namespace, this produces source compatibility with IS.NFloat the way we expect it to work in the future (e.g. x + y is now supported). When it's no longer needed, you'd simply delete the file. If you never delete it, it happily lives on side-by-side - forever. Again, this would be a choice, not a requirement. This might only be something the community provides as opposed to the platform team. Regardless, this can be used to make using NFloat today easier, while mitigating the breaking changes or obsolescence of the future.

rolfbjarne commented 2 years ago

@tannergooding

Use a different type than System.IntPtr for handles (the NSObject.Handle property). Exactly which type(s) has not been decided yet. This is to avoid confusion between nint/nuint and handles.

@rolfbjarne, sorry for the late comment on this, I had missed the original issue.

I don't think this one is a good idea.

I don't quite understand your explanation:

The rest of .NET uses IntPtr to represent handles and will continue doing so moving forward.

SafeHandle (and friends) is used in a lot of places.

As it stands "right now", IntPtr and nint have slightly different semantics.

To me that's a reason to use a different type than IntPtr (which is what nint really is) for opaque handles.

Even if that were to change, I still don't think it would be problematic as there are already operators/methods exposed on IntPtr that you wouldn't want to use in the face of true opaque handles.

Once again, this seems to be an argument for using a different type than IntPtr for opaque handles.

In any case, the problem we're running into is that we're binding native API that do something like this:

void SetValue (NSInteger number);
void SetValue (NSObject* object);

when mapping this to C#, we'd end up with something like this (if using IntPtr to represent opaque handles):

void SetValue (nint number);
void SetValue (IntPtr number);

and that won't compile, since nint and IntPtr are the same type.

rolfbjarne commented 2 years ago

nfloat -> NFloat

The only remotely feasible solution to removing our ObjCRuntime.nfloat type in favor of System.Runtime.InteropServices.NFloat would be to if NFloat's API was improved in .NET 7 to actually be usable.

In that case we could remove our ObjCRuntime.nfloat type now, and app developers could either heavily adapt their existing code to use the .NET 6 version of System.Runtime.InteropServices.NFloat, or wait until .NET 7 when the API would become a lot better.

The code adaptation in the .NET 6 time frame would be extensive and ugly...

Examples:

// before
var color = new CGColor (0.5f, 0.25f, 0.75f);
// .NET 6
var color = new CGColor (new NFloat (0.5f), new NFloat (0.25f), new NFloat (0.75f));
// .NET 7 (would work unmodified)
var color = new CGColor (0.5f, 0.25f, 0.75f);
// before
nfloat a = 1;
nfloat b = 2;
var c = a + b;
// .NET 6
var a = new NFloat (1);
var b = new NFloat (2);
var c = MathHelper.Add (a, b);
// .NET 7 (slight modifications still required)
NFloat a = 1;
NFloat b = 2;
var c = a + b;

The (eventual) upside is that we won't have two types doing pretty much the same thing, and (🔮) we won't run into any compatibility problems if C# decides to add a nfloat keyword.

@tannergooding: you seem to think that improving NFloat's API is feasible for .NET 7, who can I contact to get a commitment on this? Our deadline for removing nfloat on our side is approaching fast, and we need some sort of commitment that these changes will come in .NET 7 before even starting to do any work on our side.

tannergooding commented 2 years ago

@tannergooding: you seem to think that improving NFloat's API is feasible for .NET 7, who can I contact to get a commitment on this?

I could likely get an commitment (or not) on Tuesday for the next API review. I'd need to write up a proposal and take it through as a "blocking" request (which isn't a big deal, it just ensures it gets reviewed "first", in relation to other blocking reviews of which we currently have none).

Based on the examples you gave above, this basically just needs operators and implicit conversions support.

One interesting thing is that you allow implicit conversion from double in that sample. However, that is "potentially lossy" which goes against the "normal" implicit conversion rules. The safe thing would be:

That is:

// Implicit
NFloat x = 0.5f;
double y = x;

// Explicit
NFloat z = (NFloat)0.5;
float w = (float)z;

I'd also note that There are no NFloat constants, isn't possible unless you modified the C# lang. That is, const NFloat x = 5 won't be possible because constants for user-defined structs aren't possible today. This extends to their usage in attributes, parameters (although with the right implicit conversions and attributes parameters can work, its just more verbose), etc. The implicit conversions would work, given where there is no silent truncation.

If maintaining the silent truncation support is important, I can raise that in API review, but it is "lossy" behavior.

rolfbjarne commented 2 years ago

@tannergooding

I could likely get an commitment (or not) on Tuesday for the next API review. I'd need to write up a proposal and take it through as a "blocking" request (which isn't a big deal, it just ensures it gets reviewed "first", in relation to other blocking reviews of which we currently have none).

That sounds great!

Here's our current nfloat API: https://gist.github.com/rolfbjarne/573f32cf015e0adcab3600894e2de1a2, the closer NFloat can be the better :)

One interesting thing is that you allow implicit conversion from double in that sample.

That was a mistake in the sample code (which I've now changed to use float constants), we don't support lossy conversion in our nfloat. The double -> nfloat conversion is explicit:

https://gist.github.com/rolfbjarne/573f32cf015e0adcab3600894e2de1a2#file-nfloat-cs-L64

I'd also note that There are no NFloat constants, isn't possible unless you modified the C# lang.

Another mistake on my part, I was only thinking about conversion from constants to NFloat. nfloat as it stands today is in the same situation (it's not possible to have nfloat constants).

tannergooding commented 2 years ago

That was a mistake in the sample code

Awesome and I did notice that when looking at the existing surface area.

I've opened https://github.com/dotnet/runtime/issues/63801 to track this and left a couple annotations. Notably there were just conversions missing to/from nuint (UIntPtr), the nint/nuint conversions are "inconsistent" with explicit/implicitness of int/long and uint/ulong, we've previously opted to not extend new types with System.IConvertible, and Xamarin is currently using static readonly where-as static properties likely provide better perf and is the general "goto".

I also called out that we should likely make them inline with Half/Double/Single and have it extend the generic math interfaces that will be moving to "stable" for .NET 7. This would also expose all the math APIs like Sqrt, Min, Max, etc (what looks to be currently covered by NMath in Xamarin).

bgavrilMS commented 2 years ago

@rolfbjarne - what does this change imply for SDKs which currently target Xamarin.iOS10 ? Will you have any guidance on how to fix compatibility issues?

rolfbjarne commented 2 years ago

@bgavrilMS that will continue to work as-is, there are no breaking changes if you want to target Xamarin.iOS10. The only breaking changes are if you upgrade to .NET.

bgavrilMS commented 2 years ago

@rolfbjarne - my team owns the identity SDK. We currently target xamarin.ios10 but our customers will want to target NET6.

rolfbjarne commented 2 years ago

@bgavrilMS it's possible to target both at the same time, and ship a single NuGet that supports both.

bgavrilMS commented 2 years ago

I understand that, and we already target android9.0 and android10.0; I was hoping that there will be a guide that shows us what breaking changes there are and how to mitigate them. I am sure many SDKs will require this.

rolfbjarne commented 2 years ago

@bgavrilMS yes, we're working on a document to explain the changes and how to migrate existing code.

tannergooding commented 2 years ago

@rolfbjarne, I still have to write tests but https://github.com/dotnet/runtime/pull/64234 implements the necessary surface area and can be pulled down/built locally if you want to do any prototyping.

mattleibow commented 2 years ago

@rolfbjarne due to the changes in the type used for handles, I now get this error: https://github.com/xamarin/xamarin-macios/issues/13867

rolfbjarne commented 2 years ago

@rolfbjarne, I still have to write tests but dotnet/runtime#64234 implements the necessary surface area and can be pulled down/built locally if you want to do any prototyping.

Thanks!

rolfbjarne commented 2 years ago

All the changes we wanted to do have been implemented, so I'm closing this.

The upcoming preview 14 will have all these changes.

Barring extreme circumstances, we won't be doing any more breaking changes for subsequent preview/rc releases.

Regarding nfloat, we were able to add the operators that were missing in System.Runtime.InteropServices.NFloat into .NET 6, so we're going to remove our System.nfloat in favor of System.Runtime.InteropServices.NFloat. Most code will compile without changes, because we're automatically adding a global using to make nfloat mean System.Runtime.InteropServices.NFloat (the most common piece of code that won't compile would be any code that references nfloat using the full typename with namespace, such as: System.nfloat number = 123;)