Skip to content

Conversation

@T-Gro
Copy link
Member

@T-Gro T-Gro commented Feb 8, 2026

Description

Five nullness-related bug fixes and two regression tests for the F# compiler's nullable reference type checking.

Bug Fixes

  1. UoM ToString returns string for value types (fixes Nullness issue - ToString on an integer with UoM takes nullness information from ValueType #17539)

    • int<kg>.ToString() now returns string instead of string | null
    • Added isMeasureableValueType helper in TypedTreeOps.fs
    • Extended the ToString return-type override in MethodCalls.fs to cover measureable value types
  2. Pipe operator nullness warning location (fixes Nullness issue - Error shows on incorrect symbol when using |> #18013)

    • Nullness warnings from piped expressions (e.g. bar |> foo "mr") now point at the nullable argument (bar) instead of the pipe operator
    • Added NullnessCheckOfCapturedArg context to ConstraintSolver, propagated through CheckExpressions.fs
  3. AllowNullLiteral false positive on non-null instances (fixes Nullness issue - No way to silence warning in e.g. ServiceCollection.AddSingleton if type allows null #18021)

    • MyClass() where MyClass has [<AllowNullLiteral>] no longer triggers a false nullness warning
    • Rewrote TypeNullIsExtraValueNew to check explicit nullness annotation first, only falling back to AllowNullLiteral when nullness is ambivalent
  4. not null constraint on type extensions (fixes Nullness issue – Invalid type constraint resolution on ILookup #18334)

    • type ILookup<'Key, 'Value when 'Key : not null> with ... no longer errors
    • Added typarsAEquivWithAddedNotNullConstraintsAllowed for extension checks
    • Fixed letrec binding fixup to strip/restore extra not-null constraints during CheckTypars
  5. Tuple null elimination over-inference (fixes Nullness issue - type check in multi-match expressions doesn't eliminate null #19042)

    • match x, y with | null, _ -> ... | s, 5 -> test s no longer over-infers non-null when non-wild patterns restrict non-nullable elements
    • Simplified the tuple null elimination condition

Regression Tests (no code changes, tests only)

Changed Files (13 total)

Source (10 files):

  • src/Compiler/Checking/CheckDeclarations.fs
  • src/Compiler/Checking/ConstraintSolver.fs + .fsi
  • src/Compiler/Checking/Expressions/CheckExpressions.fs
  • src/Compiler/Checking/MethodCalls.fs
  • src/Compiler/Driver/CompilerDiagnostics.fs
  • src/Compiler/Symbols/FSharpDiagnostic.fs
  • src/Compiler/TypedTree/TypedTreeOps.fs + .fsi

Tests (3 files):

  • tests/FSharp.Compiler.ComponentTests/Language/Nullness/NullableReferenceTypesTests.fs (704 lines added)
  • 3 BSL files updated for changed error locations/baselines

Release notes:

  • docs/release-notes/.FSharp.Compiler.Service/10.0.300.md

@github-actions
Copy link
Contributor

github-actions bot commented Feb 8, 2026

✅ No release notes required

@T-Gro T-Gro force-pushed the nullness-bugs branch 8 times, most recently from ce5d2f0 to 052d236 Compare February 8, 2026 22:23
Fix #17539: UoM ToString on value types returns string instead of string|null
Fix #18013: Pipe operator nullness warning location points to nullable argument
Fix #18021: No false positive nullness warning for non-null AllowNullLiteral instances
Fix #18334: Allow 'not null' constraint on type extensions
Fix #19042: Multi-match tuple null elimination with restricting patterns

Also adds regression tests for #17727 (Option.toObj inference) and
#18034 (FSharpPlus monad CE with nullness).
@T-Gro T-Gro changed the title Fix nullness bugs: #17539, #18013, #18021, #18334, #19042 Bugfix :: Nullness :: Bug selection Feb 9, 2026
T-Gro added 14 commits February 9, 2026 17:12
…date tests

- Delete explicitNullnessOfTy helper (unsound)
- Revert TypeNullIsExtraValueNew to main branch logic using stripTyparEqns,
  tryTcrefOfAppTy, TypeHasAllowNull, nullnessOfTy, GetTyparTyIfSupportsNull
- Update 6 AllowNullLiteral tests (B1-B6) for new diagnostics
- Remove GitHub issue URL comments from test code
…yOverallType

Filter env.eContextInfo to only forward NullnessCheckOfCapturedArg context
to the constraint solver, passing NoContext for all other cases. This
prevents unintended side effects on non-nullness error formatting (e.g.
duplicated parse errors and expanded type mismatch messages in neg83.vsbsl).
…I (IsFromConstructor, KnownWithoutNullFromCtor)
… issues

- Remove Nullness.KnownFromConstructor (dead code: never constructed in production)
- Remove all handling branches in ConstraintSolver.fs, TypedTree.fs, TypedTreeBasics.fs
- Replace KnownFromConstructor-specific tests with equivalent Known WithoutNull tests
- Remove redundant comments in TypedTreeOps.fs
- Clean up blank line artifacts in .fsi files
…temThen

The proto/bootstrap build (netstandard2.0 with NO_TYPEPROVIDERS defined) fails
because 'let g = cenv.g' was moved outside the #if block but is only used
inside #if !NO_TYPEPROVIDERS code paths within TcCtorItemThen, causing
error FS1182 (unused value treated as error via --warnaserror).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment