Skip to content

Extension members solve SRTP constraints (continuation) - #19602

Merged
T-Gro merged 223 commits into
dotnet:mainfrom
gusty:feature-operators-extensions
Aug 21, 2026
Merged

T-Gro merged 223 commits into
dotnet:mainfrom
gusty:feature-operators-extensions

Conversation

@gusty

@gusty gusty commented Apr 16, 2026 •

Copy link
Copy Markdown
Contributor

Implements RFC FS-1043. Gated behind --langversion:preview.

The RFC has been updated to match this implementation in fsharp/fslang-design#835 (interop, diagnostics, compatibility, binary compat). A user guide for writing SRTP code with these features lives at docs/srtp-guide.md.

1. Extension members solve SRTP constraints

Extrinsic extension members (operators, named methods, properties) in scope at an inline function's definition site are now captured in the SRTP constraint and considered during overload resolution. Built-in primitive operator solutions retain priority when types match precisely.

type System.String with
    static member (*) (s, n: int) = String.replicate n s

let r = "ha" * 3  // "hahaha", previously FS0001

2. Weak resolution deferred for inline code

Inline SRTP functions no longer eagerly collapse to a concrete type. let inline f1 (x: DateTime) y = x + y stays generic (DateTime -> ^a -> ^b when ...). Breaking: signature files and monomorphic bindings may need updating.

3. [<AllowOverloadOnReturnType>]

New FSharp.Core attribute enables return-type-based overload resolution for any method, extending behavior previously reserved for op_Explicit/op_Implicit.

NOTE: this is a continuation from #19396 (which is now experimental zone)

T-Gro and others added 30 commits February 13, 2026 20:30
- Add LanguageFeature.ExtensionConstraintSolutions mapped to preview version
- Gate FS1215 (tcMemberOperatorDefinitionInExtrinsic) behind the feature flag
- With --langversion:preview, extension operator definitions no longer warn
- Add FSComp.txt entry and xlf translations for the feature description
…with traitCtxt field

- Add ITraitAccessorDomain marker interface to TypedTree
- Add ITraitContext interface with SelectExtensionMethods and AccessRights
- Extend TTrait with 8th field: traitCtxt: ITraitContext option
- Add TraitContext member and traitCtxtNone helper
- Make AccessorDomain implement ITraitAccessorDomain
- Update all TTrait pattern matches across the compiler (8 files)
- Pickling: traitCtxt is not serialized; deserialized as None
- No behavioral change: all trait contexts are None

Sprint 2 of RFC FS-1043 (extension method SRTP resolution)
…functions

- Add traitCtxt: ITraitContext option parameter to CopyTyparConstraints,
  FixupNewTypars, FreshenAndFixupTypars, FreshenTypeInst, FreshMethInst,
  FreshenMethInfo, FreshenTypars, CopyAndFixupTypars, FreshenPossibleForallTy,
  LightweightTcValForUsingInBuildMethodCall, FreshenTyconRef, FreshenTyconRef2,
  FreshenAbstractSlot, FreshenObjectArgType
- CopyTyparConstraints stamps traitCtxt onto MayResolveMember constraints
  when the original has None
- TcEnv implements ITraitContext with SelectExtensionMethods and AccessRights
- Add SelectExtensionMethInfosForTrait to NameResolution.fs
- Thread traitCtxtNone through all call sites (no behavioral change)
…ll call sites with TcEnv in scope

- TTrait construction sites in TcPseudoMemberSpec and TcImplicitOpItemThen
  now use env.TraitContext instead of None
- All 28 freshening call sites in CheckExpressions.fs use env.TraitContext
  (or envinner.TraitContext where the variable is named envinner)
- CheckDeclarations.fs call sites use env/envForTycon/tcEnv.TraitContext
- CheckExpressionsOps.fs CompilePatternForMatch uses env.TraitContext
- CopyTyparConstraints traitCtxt propagation is no longer dead code
- GetRelevantMethodsForTrait: collect extension methods from trait context
  when ExtensionConstraintSolutions feature is enabled
- SolveMemberConstraint: compute traitAD from trait context for accessibility
- Use traitAD in CalledMeth, ResolveOverloading, TryFindIntrinsicNamedItemOfType
- Use traitCtxt (not traitCtxtNone) in FreshenMethInfo calls
- Built-in rules use intrinsicMinfos in match pattern; when clauses use full
  minfos so extension methods properly defer built-in resolution
- traitsAEquiv already ignores traitCtxt field (verified)
… tests

- Change typecheck to compileAndRun in 'Extension operator on string resolves
  with langversion preview' test to validate runtime behavior
- Add test: FS1215 warning fires for extension operator without langversion preview
- Add test: FS1215 warning does not fire with langversion preview

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ring optimization

When FSharp.Core inline operators are expanded, the resulting trait calls have
no ITraitContext (it's not serialized in metadata). This prevented extension
methods defined in the current compilation unit from being found during
optimization/codegen, causing Error 193 type mismatches.

Changes:
- ConstraintSolver: Add guard preventing built-in mul/div rule from firing
  for concrete non-numeric types when ExtensionConstraintSolutions is enabled
- ConstraintSolver: Add CreateImplFileTraitContext that walks the expression
  tree bindings (not CcuThunk module type) to find extension member Val objects
  with correct stamps matching IlxGen's bound vals
- Optimizer: Add traitCtxt field to cenv, supply fallback trait context in
  OptimizeTraitCall when trait's original context is None

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Test B5: Multiple extension operators with different signatures resolve correctly
- Test B6: Intrinsic operator takes priority over extension with same name and signature

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 5 new tests covering:
- Extension operator resolves via SRTP alongside IWSAM types (B7)
- Extension wins over IWSAM interface impl for same operator (B7)
- Extension operator not visible without opening defining module (B9)
- Inline SRTP resolves using consumer's scope for extensions (B9)
- Internal record field resolves via SRTP within same compilation unit (B10)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Test 1: True optional extension on System.String (*) across assemblies.
  Documents that cross-assembly extension resolution fails (Error 193)
  because TTrait.traitCtxt deserializes as None from pickled metadata.
- Test 2: Intrinsic augmentation Widget (+) across assemblies.
  Confirms intrinsic operators work cross-assembly (for contrast).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove three tests that were near-identical duplicates exercising the same
'define record + extension (+) + inline add' pattern:
- 'Inline SRTP function uses extension method on custom type' (duplicate of 1910)
- 'Extension operator resolves via SRTP alongside IWSAM types' (duplicate, misleading name)
- 'Extension operator on custom type typechecks in separate module' (subset of Intrinsic method priority test)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add defensive type-test assertions replacing unsafe obj downcasts in
  CheckBasics.fs (InfoReader), ConstraintSolver.fs (AccessorDomain, MethInfo)
- Hoist ExtensionConstraintSolutions feature flag check to local binding
  in SolveMemberConstraint and GetRelevantMethodsForTrait
- Extract SelectExtMethInfosForType shared helper in NameResolution.fs
  and refactor SelectExtensionMethInfosForTrait to use it

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… resolution

Define AllowOverloadOnReturnTypeAttribute in FSharp.Core and wire it into
the compiler's overload resolution and uniqueness checking. When any
applicable overload carries this attribute, the return type is considered
during overload resolution, generalizing the existing op_Explicit/op_Implicit
mechanism to arbitrary methods.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eftover files

Update FSharp.Core surface area baseline to include the new
AllowOverloadOnReturnTypeAttribute type. Remove leftover code review
artifact files that were accidentally committed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…InfosForType

Extract SelectIndexedExtMethInfosForType helper for indexed-only extension
member lookup. Refactor ExtensionMethInfosOfTypeInScope to use
SelectExtMethInfosForType for the root type (both indexed and unindexed
members) and SelectIndexedExtMethInfosForType for base types in the
hierarchy (indexed only), removing the duplicated tryTcrefOfAppTy /
eIndexedExtensionMembers.Find / SelectMethInfosFromExtMembers pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sForType

Eliminate code duplication by having SelectExtMethInfosForType delegate its
indexed lookup to SelectIndexedExtMethInfosForType instead of duplicating
the tryTcrefOfAppTy/Find/SelectMethInfosFromExtMembers pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ions

Skip SRTP constraint canonicalization for inline bindings when the
ExtensionConstraintSolutions feature is enabled (--langversion:preview).
This implements RFC FS-1043 claim dotnet#6: weak resolution should not force
inline code to collapse generic SRTP constraints to concrete types.

Changes:
- CheckExpressions.fs: Skip CanonicalizePartialInferenceProblem for inline
  bindings in TcLetBinding, TcObjectExprBinding, and
  TcIncrementalLetRecGeneralization when ExtensionConstraintSolutions is on.
- IWSAMsAndSRTPsTests.fs: Add 6 tests covering inline DateTime generics,
  non-inline unaffected, backward compat with langversion 8.0, and
  built-in numeric operators.

Non-inline code is completely unaffected. Gated behind langversion:preview.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enhance CreateImplFileTraitContext to search referenced CCU module types
for extension member Vals, not just local impl file bindings. This enables
cross-assembly SRTP constraint resolution during optimization/codegen when
extension operators are defined in a referenced assembly.

Changes:
- ConstraintSolver.CreateImplFileTraitContext: accept CcuThunk list, walk
  referenced CCU module types to collect extension members
- Optimizer.IncrementalOptimizationEnv: add referencedCcus field populated
  by BindCcu
- Optimizer.OptimizeImplFile: pass referencedCcus to CreateImplFileTraitContext
- Tests: change cross-assembly test from shouldFail to compileAndRun/shouldSucceed,
  remove TODO comment, add transitive A→B→C test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove Skip from 'AllowOverloadOnReturnType resolves overloads by return
type' test now that AllowOverloadOnReturnTypeAttribute is available in
FSharp.Core.

Add test for ambiguity error when no type annotation is present.
Add test for mixed attributed/non-attributed overloads.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 4 tests covering SRTP weak resolution behavior with langversion preview:
- FSharpPlus-style InvokeMap/InvokeApply pattern compiles with preview
- Type annotation workaround for InvokeMap pattern compiles
- Non-inline code canonicalization unaffected by ExtensionConstraintSolutions
- Inline numeric operators with multiple overloads stay generic

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nType assertions

- Remove duplicate 'Non-inline DateTime addition still resolves concretely'
  test (identical to 'Non-inline code canonicalization is unaffected')
- Fix 'AllowOverloadOnReturnType resolves overloads by return type' to assert
  shouldFail (attribute not yet functional for return-type disambiguation)
- Fix 'AllowOverloadOnReturnType mixed' to assert shouldFail (attribute type
  not yet defined in FSharp.Core)

All 227 IWSAM/SRTP tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 4 tests covering instance extension methods with SRTP constraints:
- Instance extension method resolves via SRTP
- Instance extension method with parameter resolves via SRTP
- Instance extension does not satisfy static SRTP constraint
- Intrinsic instance method takes priority over instance extension

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace 3 failwith calls with error(InternalError(...)) in ITraitContext
downcast code paths to produce proper compiler diagnostics instead of
opaque exceptions:
- CheckBasics.fs: SelectExtensionMethods InfoReader cast
- ConstraintSolver.fs: SolveMemberConstraint AccessorDomain cast
- ConstraintSolver.fs: GetRelevantMethodsForTrait MethInfo cast

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 4 new tests covering accessibility-domain scenarios for SRTP:
- Internal type extension in same assembly resolves via SRTP
- SRTP constraints from different accessibility domains flow together
- Internal record field resolves via SRTP within same assembly
- Cross-assembly internal extension is not visible via SRTP

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename _collectionSettings back to collectionSettings and add early-exit
check after rootResults to skip expensive type hierarchy lookup when only
one result is needed (AtMostOneResult). This restores the perf optimization
that was lost during RFC FS-1043 refactoring.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Document extension members for operators and SRTP constraints in preview release notes
- Add SRTP guide covering extension constraints, priority rules, scope capture, weak resolution changes, and workarounds
- Feature flag: ExtensionConstraintSolutions (--langversion:preview)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove false 'known limitation' about cross-assembly resolution from
  release notes and srtp-guide.md (cross-assembly resolution works)
- Clarify AllowOverloadOnReturnType: attribute allows defining overloads,
  but full call-site disambiguation is not yet implemented
- Add cross-assembly resolution as an explicit feature bullet in release notes
- Fix stale test comment about AllowOverloadOnReturnType not being in FSharp.Core

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add AllowOverloadOnReturnTypeAttribute to all surface area baselines
  (debug ns2.0, debug ns2.1, release ns2.0 were missing the entry)
- Add withErrorCode to all 7 shouldFail tests without error verification:
  - Extension operator not visible: error 1
  - AllowOverloadOnReturnType overloads by return type: error 41
  - Overloads without AllowOverloadOnReturnType: error 41
  - AllowOverloadOnReturnType with no annotation: error 41
  - Instance extension does not satisfy static SRTP: error 1
  - Cross-assembly internal extension not visible: error 43
- Fix mixed overloads test: overloads with different param types (string
  vs int) resolve without ambiguity — changed to shouldSucceed
- Remove AllowOverloadOnReturnType attribute from shouldFail tests since
  the attribute is not available in the test runtime's FSharp.Core
- Parameterize DateTime addition/subtraction tests into a single Theory
- Extract stringRepeatExtLib helper to eliminate duplicated string
  extension operator library definitions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nition

- Merge 'AllowOverloadOnReturnType resolves overloads by return type' and
  'Overloads without AllowOverloadOnReturnType produce ambiguity error'
  into single 'Overloads differing only by return type produce ambiguity
  error' test (identical source and assertions)
- Extract stringRepeatExtDef [<Literal>] constant for inline string
  repeat extension definition, reducing 5 inline duplicates to 1
- Update stringRepeatExtLib to reuse stringRepeatExtDef

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bartelink pushed a commit to bartelink/fsharp that referenced this pull request Oct 7, 2026
* Fix FCS docs scripts broken by API changes

The literate scripts under docs/fcs no longer compiled against the
current FSharp.Compiler.Service, so the published tutorial pages
rendered compiler errors instead of output. fsdocs 23 surfaces script
evaluation failures rather than discarding them, which made this visible.

- tokenizer.fsx: FSharpSourceTokenizer takes three arguments now, drop
  the trailing None and describe the language version argument.
- editor.fsx: FSharpMethodGroupItemParameter.Display is a RichText, so
  read its Text instead of enumerating tagged parts.
- typedtree.fsx: annotate the input as string to disambiguate the new
  File.WriteAllText(string, ReadOnlySpan<char>) overload.
- untypedtree.fsx: SynExpr.LetOrUse is a SynLetOrUse record and
  SynModuleDecl.Let gained a trivia field.
- untypedtree-apis.fsx: SynComponentInfo lost its longId field in
  dotnet#19602, use the LongIdent compatibility member.

* Map release notes to the packages that actually shipped

The release notes pages derived the package version from the notes
file name, which no longer holds. FSharp.Compiler.Service bumps its
minor version independently of the F# version (43.12.100 is F# 11.0.100
while 43.12.204 is F# 10.0.204), and FSharp.Core 10.0.2xx and 10.0.3xx
shipped as 10.1.x. Recent headings therefore named versions that do not
exist and showed as unreleased.

Each package on NuGet is now matched to its notes file through the
source commit recorded in its nuspec: eng/Versions.props at that commit
(under src/fsharp for VMR builds) gives the exact F# version. The
heading shows the first package that shipped for the notes, with a
badge per servicing rebuild, and the page is ordered by package version.

Also:
- Sort versions numerically instead of as strings, so 10.x and 11.x no
  longer sort below 9.x. The Language page had a broken comparer that
  threw on three-part versions and rendered nothing.
- Distinguish unlisted packages, which NuGet reports with a 1900-01-01
  publish date, from ones that are not on NuGet at all.
- Silence FsHttp request logging and bump Markdig and FsHttp.

* Give every docs page a category in the published navigation

Twelve pages had no fsdocs front matter, so the published site listed
them in an unnamed "Other" group at the bottom of the navigation.

The postmortems get their own "Postmortems" category, with the README as
the overview. The remaining pages join the existing categories: the
equality optimizations, regression testing, LabelOps, perf archive and
SRTP guide under Compiler Internals, the LSP proposal under Language
Service Internals, reflection-free printing under FSharp.Core and the
pending breaking changes under Release Notes.

The postmortem skill now tells the agent to emit that front matter, and
the postmortems README no longer links to ../../.github, which cannot
resolve on the site.

The "running the documentation locally" guide is rewritten for the
build.fsx pipeline in fsharp-compiler-docs and its FSHARP_REPO variable,
and documents how fsdocs watch caches pages and why edits to the
release-notes markdown are not picked up.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚠️ Affects-Compiler-Output Tooling check: PR touches IL emission or codegen ⚠️ Affects-Test-Tooling Tooling check: PR touches test framework infrastructure AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

4 participants