Implement RequireNamedArgumentsAttribute (FS-1095) - #20340
Conversation
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
✅ Release notes checked
|
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
…edArgumentAttribute Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0ba0e7d9-1886-4d3b-9dd8-dee813ee144b
|
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. |
|
Funny you should mention that, I was just thinking of writing an Analyzer to make use of this in C# too =) |
|
What is the difference between the first and the third? I guess I do not understand Either way, if the feature is seen as obtrusive those heuristics by @CyrusNajmabadi could be accepted to the FS-1095 RFC. |
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>
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>
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>
|
@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>
Head branch was pushed to by a user without write access
Merged main and resolved the conflict in |
|
@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>
Merged main and resolved the conflict in |


Implements RFC FS-1095 (fsharp/fslang-suggestions#414). Methods and constructors annotated with
System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttributerequire 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
AttributeUsagedoes not extend this PR's F# enforcement to parameters, delegates or property/indexer syntax.