Skip to content

Implement RequireNamedArgumentsAttribute (FS-1095) - #20340

Merged
T-Gro merged 29 commits into
mainfrom
t-gro-require-named-argument-research
Sep 19, 2026
Merged

T-Gro merged 29 commits into
mainfrom
t-gro-require-named-argument-research

Conversation

@T-Gro

@T-Gro T-Gro commented Aug 24, 2026

Copy link
Copy Markdown
Member

Implements RFC FS-1095 (fsharp/fslang-suggestions#414). Methods and constructors annotated with System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute require named arguments under --langversion:preview; positional calls report FS3918.

The API name, namespace and declaration follow the approved dotnet/runtime#132924 API (RFC update: fsharp/fslang-design#856). Recognition uses the full type name, including user-supplied polyfills in the same compilation or referenced F#/C# assemblies; the compiler does not synthesize the attribute.

The broader approved AttributeUsage does not extend this PR's F# enforcement to parameters, delegates or property/indexer syntax.

T-Gro and others added 14 commits August 20, 2026 10:43
Recognise System.Runtime.CompilerServices.RequireNamedArgumentAttribute by
full type name only (polyfill), so it is honoured whether defined in the
runtime assembly, the same compilation unit, or a different assembly. A method
annotated with it must be called using named-argument syntax; positional
(unnamed) caller arguments are rejected with error FS3910. Zero-argument calls
are unaffected. Gated behind the RequireNamedArgument preview language feature.

- WellKnownILAttributes / WellKnownValAttributes bits + classifier entries
- Generalise resolveAttribPath to resolve a local type's PublicPath so
  same-compilation-unit polyfills are classified
- TcGlobals attrib_RequireNamedArgumentAttribute handle (findSysAttrib)
- LanguageFeature.RequireNamedArgument (preview)
- FSComp diagnostic 3910 tcMethodRequiresNamedArguments + xlf
- Enforcement in TcMethodApplication STEP 4
- ComponentTests: runtime/same-ccu/different-assembly x F#/IL matrix,
  zero-arg, optional, interface, langversion-off cases

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
Round-1 review council (NoBloat, CodeCompaction, Expert-review, interop
test-coverage) unanimously flagged two blocking issues in the initial
RequireNamedArgumentAttribute implementation:

[16] Recognition was broadened generically via resolveAttribPath, which
made ~30 unrelated BCL well-known attributes (CallerLineNumber, IsReadOnly,
Extension, ...) newly recognised for same-compilation-unit definitions under
released langversions - a real behavioural regression. Revert the generic
resolveAttribPath broadening and the well-known-attribute bit machinery;
recognise the attribute with a scoped, name-based MethInfoHasAttributeByName
(mirrors the FS-1053 TyconRefHasAttributeByName precedent). One uniform
by-name check covers all provenance: runtime assembly, same compilation unit,
and any other assembly - with zero blast radius.

[17] ParamArray hole: TotalNumUnnamedCallerArgs is 0 when every positional
caller argument is captured by a ParamArray, so 'C.Sum(1,2,3)' skipped the
check. Enforce on NumUnnamedCallerArgs > 0 OR non-empty ParamArrayCallerArgs.

Tests: add negative-recognition (same simple name, different namespace; F#
and C#/IL), mixed named/positional, and ParamArray positional/named cases.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
…ion, first-class)

Round-1 council tie-breaks (ducks A/B/C across gpt-5.6-sol, gemini-3.1-pro,
claude-opus-4.8):
- [7] overload resolution -> ADDRESS (A+B): enforcement runs after overload
  selection; positional call to the unannotated overload succeeds, to the
  annotated overload fails.
- [8] extension methods -> proven green and kept: F#-style extension member and
  C#-style extension method (ILMeth); the receiver 'this' is not counted as a
  positional caller argument.
- [18] first-class use -> ADDRESS (A+C, codify rejection): 'let f = C.Add'
  errors FS3910; explicit named forwarding 'let f x y = C.Add(x=x, y=y)' works.
- [11],[14],[15] -> REJECT (majority): by-name check is provenance-agnostic;
  named-before-positional is invalid F# syntax; module comment is accurate.

24 RequireNamedArgument tests pass; well-known-attribute regression suites
(byref/readonly/callerinfo/ORP 27, Span/Attribute/Extension 643) green.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
…diagnostic

Round-2 fixes:
- Guard RequireNamedArgument enforcement with AssociatedPropertyInfo.IsNone and
  NumArgSets <= 1 so property/indexer accessors and curried members (which have no
  named-argument form) are not made uncallable.
- Constructor diagnostics now name the type (via ApparentEnclosingTyconRef.DisplayName)
  instead of leaking the internal '.ctor' name.
- Fix stale test fixture comments referencing the reverted cached-flags path.
- Add regression tests: indexer getter/setter, property setter, curried member,
  constructor positional (names type) and named-call success.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
- Trim enforcement comment (council [102], 2-1) keeping the guard rationale.
- Add C# extension-method + ParamArray coverage (council [106]).
- Add C# optional-parameter coverage (council [108]).
- Add generic annotated-method coverage (council [109]).
All 36 feature tests pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
Adds three regression tests approved by the Round-3 review council, all
locking distinct MethInfo resolution paths against a positional-call bypass:

- C# interface slot + class override (virtual/interface-slot path)
- C# imported value-type (struct) constructor path
- method-group-to-delegate coercion (Func<int,int>(C.Ping)) still enforces

Also documents the computation-expression custom-operation limitation
(no named form, intentionally not carved out) noted by the expert reviewer.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
Red-green verification showed the F# primary-constructor test never guarded
the .ctor-name fix (an F# ctor's LogicalName is already the type name). The
imported C# (ILMeth) constructor is the path whose LogicalName is '.ctor', so
assert the struct-constructor diagnostic names the enclosing type 'S' - this
is the test that genuinely fails if the constructor-name branch regresses.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
…+ imported attribute)

Every prior test co-located the attribute with the annotated method. This adds
the missing off-diagonal: a local F# method (same compilation unit) annotated
with the attribute *type* imported from a referenced assembly - the canonical
scenario once the BCL ships the attribute and you annotate your own method.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
Recognise RequireNamedArgumentAttribute via the cached well-known-attribute
flags (WellKnownILAttributes bit 27 + WellKnownValAttributes bit 43 with the
matching classifyILAttrib/classifyValAttrib name entries) instead of a
per-call by-name attribute scan.

- Generalise the existing MethInfoHasWellKnownAttribute to take a
  BuiltinAttribInfo voption, so a polyfill with no BuiltinAttribInfo handle
  passes ValueNone; drop the hand-rolled MethInfoHasRequireNamedArgumentAttribute
  that duplicated its MethInfo dispatch.
- Extend resolveAttribPath so a same-compilation-unit polyfill's local ref
  resolves its PublicPath (symmetric with the compilingFSharpCore branch).
- Extract the call-site guard into CalledMeth.TryGetRequireNamedArgumentViolationName,
  reusing the existing TotalNumUnnamedCallerArgs/ParamArrayCallerArgs accessors
  rather than re-folding ArgSets; the O(1) attribute test runs first.
- Remove the dead tname_RequireNamedArgumentAttribute literal and trim
  restating comments across the feature and its tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
… C# polyfill

Rubber-duck panel (3 cross-model) consensus, zero-locality-cost wins only:
- Extract rejectsPositional/acceptsNamed/rejectsCompiled/acceptsCompiled to
  collapse the ~40 repeated 5-line assertion tails (kills magic 3910 and the
  trailing |> ignore noise); the 3 bespoke tails (v9.0 gate, two message
  assertions) stay explicit.
- Extract csPolyfill/csharpWithPolyfill for the Method-target C# fixtures.
- Fix a factually wrong test name (interface implementation, not override).

43 tests unchanged and green; -140 LOC.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
The F# (fsPolyfill / withPolyfillCtor) and C# (csPolyfill / csStructCtorLib)
attribute declarations were near-identical, differing only in AttributeTargets.
Introduce parameterized templates (fsPolyfillTargeting / csPolyfillTargeting +
csharpWithPolyfillTargeting) so each attribute source has a single definition.
Only the two intentional wrong-namespace (MyApp) negatives remain as separate
declarations, by design.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
The FS-1095 RequireNamedArgumentAttribute does not exist in the BCL
(dotnet/runtime#51451 is only a proposal); the feature recognises a
user-defined attribute by full type name. Two comments wrongly implied a
"real Method-only BCL attribute" exists. Reword both to describe the
mechanism (recognition by full type name / an AttributeUsage guard) without
asserting a BCL original.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
The compile-path tests each pay C#-lib-compile + full-compile overhead, and
many referenced the same C# library. Merge, per shared library, the
positional-error call sites into one compile and the named-success call sites
into one compile:
- csAnnotatedLib: 4 tests -> 2 (plain IL method + optional-arg method)
- csExtensionLib: 4 tests -> 2 (plain extension + ParamArray)
- csInterfaceLib: 3 tests -> 2 (slot + concrete receiver)

Rigor is preserved via a new count-exact helper `rejectsAllCompiled`, which
uses `withErrorMessages` (asserts exact error count AND each FS3910 message,
whose method name identifies each merged call site). 43 -> 38 tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
Merge the ~22 same-compilation-unit typecheck cases (each repeating the
polyfill + a near-identical annotated type) into a single shared `annotatedApi`
zoo plus one positive and one negative grouped test. Distinct member names keep
the merged negative fully diagnostic via count-exact `withErrorMessages`.

38 -> 18 tests, all coverage lines preserved.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

✅ Release notes checked


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
`src/Compiler/Facilities/LanguageFeatures.fsi` docs/release-notes/.Language/preview.md

T-Gro and others added 2 commits August 24, 2026 16:58
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
…rgument-research

# Conflicts:
#	src/Compiler/FSComp.txt
#	src/Compiler/xlf/FSComp.txt.cs.xlf
#	src/Compiler/xlf/FSComp.txt.de.xlf
#	src/Compiler/xlf/FSComp.txt.es.xlf
#	src/Compiler/xlf/FSComp.txt.fr.xlf
#	src/Compiler/xlf/FSComp.txt.it.xlf
#	src/Compiler/xlf/FSComp.txt.ja.xlf
#	src/Compiler/xlf/FSComp.txt.ko.xlf
#	src/Compiler/xlf/FSComp.txt.pl.xlf
#	src/Compiler/xlf/FSComp.txt.pt-BR.xlf
#	src/Compiler/xlf/FSComp.txt.ru.xlf
#	src/Compiler/xlf/FSComp.txt.tr.xlf
#	src/Compiler/xlf/FSComp.txt.zh-Hans.xlf
#	src/Compiler/xlf/FSComp.txt.zh-Hant.xlf
@T-Gro
T-Gro requested a review from abonie August 25, 2026 08:56
…edArgumentAttribute

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
@T-Gro
T-Gro marked this pull request as ready for review August 25, 2026 11:48
@T-Gro
T-Gro requested a review from a team as a code owner August 25, 2026 11:48
@T-Gro
T-Gro enabled auto-merge (squash) August 25, 2026 11:48
@github-actions github-actions Bot added the AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed label Aug 25, 2026
@CyrusNajmabadi

Copy link
Copy Markdown

i think this should potentially be discussed with C#. The attribute doesn't say it is F# specific. So i imagine people would (rightly) expect that if they used this attribute they would get it enforced in C# as well.

@yaakov-h

Copy link
Copy Markdown
Member

Funny you should mention that, I was just thinking of writing an Analyzer to make use of this in C# too =)

@CyrusNajmabadi

Copy link
Copy Markdown

Note: i don't like the f# proposal currently. it would require doing htings like this in C#: Foo(dividend: dividend, divisor: divisor). I think we should have better control of this, like we do with "Inlay Hints":

image

@T-Gro

T-Gro commented Aug 26, 2026

Copy link
Copy Markdown
Member Author
image

What is the difference between the first and the third? I guess I do not understand method's intent...

Either way, if the feature is seen as obtrusive those heuristics by @CyrusNajmabadi could be accepted to the FS-1095 RFC.
It is mainly an API design tool for avoid correctness risks where API author sees them.

@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Sep 9, 2026
perf-bundle and others added 3 commits September 10, 2026 11:39
Scope local attribute recognition, enforce provided method metadata, and exempt nameof operands without exempting first-class methods.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep the single-diagnostic assertion and include collection contents in its failure output.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Sep 16, 2026
Copilot and others added 4 commits September 16, 2026 15:15
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep forwarding explicitly non-inline after List.forall2 became inline. Check only Test.test, retain a call-site local-closure positive control, and reject unrelated helper allocations and missing methods.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Unix ILDASM driver accepts only a hyphen-prefixed switch; a slash is parsed as a second input filename. Reproduced the failure with the native Linux ILDASM and verified method isolation, allocation controls, and missing-method output with -item=.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@T-Gro T-Gro changed the title Implement RequireNamedArgumentAttribute (FS-1095) Implement RequireNamedArgumentsAttribute (FS-1095) Sep 16, 2026
Use the approved System.Diagnostics.CodeAnalysis identity and polyfill shape without extending F# preview enforcement.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@T-Gro

T-Gro commented Sep 17, 2026

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

…rgument-research

# Conflicts:
#	tests/FSharp.Compiler.ComponentTests/EmittedIL/Inlining/InlineIfLambdaClosureForms.fs

Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
auto-merge was automatically disabled September 17, 2026 11:16

Head branch was pushed to by a user without write access

Copilot AI commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Merged main and resolved the conflict in InlineIfLambdaClosureForms.fs (unrelated test file) by adopting main's version. Build and the RequireNamedArguments / InlineIfLambda tests pass.

Comment thread src/Compiler/FSComp.txt Outdated
@T-Gro

T-Gro commented Sep 18, 2026

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request. The id of text message cannot be 3918 anymore, so it has to move to next available slot and also any usages in tests have to be adjusted

…rgument-research

# Conflicts:
#	src/Compiler/FSComp.txt

Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>

Copilot AI commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request. The id of text message cannot be 3918 anymore, so it has to move to next available slot and also any usages in tests have to be adjusted

Merged main and resolved the conflict in FSComp.txt. main had taken 3918 for the new runtime-async diagnostics (3918-3922), so tcMethodRequiresNamedArguments now uses the next free slot, FS3923. Updated all test usages (RequireNamedArgumentsTests.fs, NamedArguments.fs) and release notes accordingly; unrelated FS3918 usages in the runtime-async tests were left untouched. Build and all relevant tests (RequireNamedArguments, NamedArguments, RuntimeAsync, InlineIfLambda) pass.

@T-Gro
T-Gro merged commit 0bde38c into main Sep 19, 2026
52 checks passed
@T-Gro
T-Gro deleted the t-gro-require-named-argument-research branch September 19, 2026 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-reviewed PR reviewed by AI review council AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

8 participants