Skip to content
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

[WIP] [RFC FS-1060] Nullness checking #15181

Draft
wants to merge 173 commits into
base: main
Choose a base branch
from
Draft

[WIP] [RFC FS-1060] Nullness checking #15181

wants to merge 173 commits into from

Conversation

dsyme
Copy link
Contributor

@dsyme dsyme commented May 3, 2023

Continuation of #5790 and #6804

This is a prototype implementation of RFC FS-1060 nullable reference types

See tests\adhoc\nullness for testing and samples including baselines of outputs from

  • existing compiler
  • updated compiler
  • updated compiler with /langversion:preview
  • updated compiler with /langversion:preview /checknulls

TODO:

  • revise RFC and resolve all unresolved issues (@dsyme)
  • implement syntax string | null and : not null (@T-Gro )
  • implement import of .NET metadata (@T-Gro )
  • implement emit of .NET metadata (@T-Gro )
  • Import+Export TODOs (to test and adjust if needed)
  • Nullness used in inheritance (e.g. inherit System.Collections.Generic.List<string | null>)
  • Soundness of import of CLI EventHandlers (possibly overly defensive)
  • Nullness and import of TypeProvider-generated types + In general the overall TP scenario whet it comes to nullness support - do in separate PR
  • Object overrides can change nullness (e.g. boolean.ToString() does or StringBuilder.ToString()), make it work to avoid false alarms
  • Type.GetType(..) |> Option.ofObj produces a false alarm, fix that
  • printfn "%s" someNullableString produces a false alarm, check the printing pattern annotations
  • (+) operator for strings gives false alarms on nullable strings, should allow nullables since String.Concat also does
  • Pattern matching flow analysis on match x with | null -> Support this as a scenario
  • Allow incoming arrays to be null (this causes false alarms as of now, it's a bug)
  • resolve all // TODO NULLNESS (@dsyme)
  • use it in the codebase and get it green
  • there's a case in tests\adhoc\nullness we're getting The types 'System.String (...)' and 'System.String (...)' do not have compatible nullability. which is wrong - either the nullability aren't shown for some reason or the types should be considered compatible

Testing:

  • add proper testing, moving tests across from tests\adhoc\nullness
  • test and check signature compatibility. While integrating master I noticed a case in the compiler where a signature file had a non-nullable string type for ther return of a function and an implementation had a nullable string type (and a later soundness problem arose)

To be moved to RFC and resolved, then tested here:

  • should nullness be supported on ref tuples and anon tuples
  • work out what to do about compat of Option.ofObj and others
  • the "type equiv" relation is currently directional only giving a nullness warning if "actual" has a null and "expected" is non-null. This includes a dubious change of direction in SolveFunType thing - for the contravariance of inputs. This has caused a few outputs to change in tests. This is both unsound in the general case (nested cases of equivalence should demand true equivalence) and is dubious for functions because the "MatchingOnly" case of type equivalence is also directional.
  • assess nullness checking in conditionals, e..g if x then null else "" and if x then "" else null

@dsyme dsyme requested a review from a team as a code owner May 3, 2023 13:22
@dsyme
Copy link
Contributor Author

dsyme commented May 6, 2023

@vzarytovskii @T-Gro I squahed this and also fixed a nasty bug with nullness inference that I spotted while walking through with Vlad on Thursday

@dsyme
Copy link
Contributor Author

dsyme commented May 25, 2023

@vzarytovskii @T-Gro This is basically green now :) And much, much trimmed down, with some bugs fixed. I've also revised the TODO list to what would be needed for bringing into preview

(It's possible we could even ship preview using the string __withnull syntax then incrementally improve after)

@vzarytovskii
Copy link
Member

@vzarytovskii @T-Gro This is basically green now :) And much, much trimmed down, with some bugs fixed. I've also revised the TODO list to what would be needed for bringing into preview

I'm going to take care of metadata part

* Bugfix - matching aliased nullable should strip nullness

Eliminating nullness after pattern matching null (that is , for subsequent patterns) must visit contents of abbreviations as well. Otherwise it does not work with the Maybe<T> type whcih we use in the compiler.

* Making 'obj' work with new 'not null' constraints in fslib functions

Bugfix: obj cannot be passed to generic typars which require T: not null, such as the NonNull active pattern.

This commit fixes it.

* Bugfix - false 'useless null' warning in nested applications

Error fixed:
Error on useless null checkwith nullness constraint propagation in code like this:let meTry = Option.ofObj (Path.GetDirectoryName "")`. The warning about 'useless Option.ofObj' points to the string literal, ignoring the string literal is first passed to an API which may return null

* Fix import for C# extension methods

Bugfix for:
C# extension methods which put "?" on the this argument are wrongly interpreted by moving the nullability elsewhere. See AsMemory<T> from System.Memory.dll , this treats byteArray.ToMemory() as resulting in a Memory<byte | null> which is clearly wrong.

Also, this now allows to call C# extension methods with ?this to be invoked on a nullable value.

* LinkedList First,Last bugfix

There was a bug of LinkedList .First and .Last properties not returning nullable nodes.
This was fixed by improved byte import in previous commit, adding a regression test for guarding this.


* Bugfix: Solve nullness for typars

This fixes a bug where `not null` generic constraint was incorrectly passed between two typars:`T1 | null` with not null constraint on T1, and T2 without constraints.

This occured when calling Option.ofObj(..) when the inner expression caused solving of generic type arguments, e.g. after (|>) or (id) function.

This uses additional inference variable to unify them.
@T-Gro T-Gro modified the milestones: April-2024, May-2024 May 2, 2024
@ken-okabe
Copy link

ken-okabe commented May 6, 2024

I came across an article about F# Nullness support . As someone who's eagerly awaiting Nullable types including reference types in F#, I want to thank you for this contributions.

https://github.com/ken-okabe/vanfs?tab=readme-ov-file#nullable
https://github.com/ken-okabe/vanfs/blob/main/README-whatisNull.md

T-Gro added 18 commits May 16, 2024 14:18
Fix issues for plain 'dotnet build Fsharp.Compiler.Service.sln`
* Ignore Nullness applied on structs (C# allows T? when when T is a struct)
* Bigfix: Working with CLI events in Fsharp
* Bugfix: Mutable binding initially assigned to null should not need type annotation
* Solving `let mutable cache = null` via type inference
* Enforcing TyparConstraint.IsReferenceType when WithNull type is used
* Nullness-related constraint consistency
* Bugfix for emitting Nullable attrs for C#
@T-Gro T-Gro modified the milestones: May-2024, June-2024 Jun 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: New
Development

Successfully merging this pull request may close these issues.

None yet

9 participants