-
Notifications
You must be signed in to change notification settings - Fork 790
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
String operator always returning nonNull string #17809
String operator always returning nonNull string #17809
Conversation
This was supposed to say "will now return an empty string" (and "all code paths" by implication meant "all code paths that returned null before"), right? Otherwise I don't get it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be clear — this is an actual change in behavior, not just a change in nullness inference, right?
Is this accurate?
Before
// Before: both returned null.
string { new obj () with override _.ToString () = null }
string { new IFormattable with override _.ToString (_, _) = null }
After
// After: both return "".
string { new obj () with override _.ToString () = null }
string { new IFormattable with override _.ToString (_, _) = null }
This is technically a breaking change, so it seems like it should probably should have a language suggestion (e.g., compare fsharp/fslang-suggestions#891).
I'd also be curious what the perf implications are, if any, of the extra IL and branches this adds to string
calls. It would be a shame to make 99.99% of code using string
slower just to make the nullness info accurate in the extremely rare scenario of someone intentionally overriding ToString
to return null
.
Yes, this bugfix (or handling a forgotten scenario) is a breaking change (I guess technically, every bugfix is). Will have to be documented as such.
We first have to make it correct. |
Do we know if this is described in any rfc or spec?
What the rough IL we will be generating here? |
I did not find any mention of the behavior for the string function, a promise about exactly returning .ToString() is not there. The IL will look like this:
For all normally-behaving objects, branch prediction will be stable for the brtrue.s check. |
Yeah, I would think so as well. Will be curious to hear from @EgorBo as well |
C# does it like this for
Which uses less instructions for the happy path (ToString result not being null). |
❗ Release notes required
|
The fix is clear, but I think it would be nice to add some IL baselines for this behavior. |
Suggestion approved now: fsharp/fslang-suggestions#1386 |
/azp run |
Azure Pipelines successfully started running 2 pipeline(s). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @psfinaki about adding a simple test that covers this behavior.
Co-authored-by: Petr Pokorny <petrpokorny@microsoft.com>
This fixes #17742 .
F#-originating code has always been against implicit nulls.
Even when null literal was passed into the string function, the null was still replaced with an empty string.
What was missing was checking for object types overriding ToString and returning null out of their implementation - this has not been historically checked, very likely due to this pattern not being heavily used and possibly overlooked in the implementation of the string operator.
This PR fixes it by making sure that all code paths of the statically optimized
string
call will not return an empty string.The alternative has been evaluated and changing the signature to
let inline string x : string | null = ...
would flood all legitimate usages of this standard function and enforce unnecessary null checks (under the assumption that legitimate F# code does not return null from .ToString, which is even a new warning now)