Skip to content

RFC FS-1354 - Struct-ness inference for tuple patterns - #857

Open
xperiandri wants to merge 2 commits into
fsharp:mainfrom
xperiandri:rfc/fs-1354-struct-tuple-pattern-inference
Open

xperiandri wants to merge 2 commits into
fsharp:mainfrom
xperiandri:rfc/fs-1354-struct-tuple-pattern-inference

Conversation

@xperiandri

Copy link
Copy Markdown
Contributor

Click “Files changed” → “⋯” → “View file” for the rendered RFC.

Summary

A tuple pattern without struct gets its kind (struct or reference) from type inference, so code after the pattern can decide it. If nothing decides it by the end of the declaration, the kind is reference, as today. This follows Don Syme's design on #988 (an inference variable for the tuple kind) and implements the "structness inference" extension of FS-1006 for patterns.

let s, c = Math.SinCos 1.0                              // today FS0193
xs |> Array.iter (fun (x, y) -> printfn "%d %d" x y)    // xs: struct (int * int)[]; today FS0001
let g v = let a, b = v in takesStruct v                 // today FS0001

Programs that compile today keep their meaning: every step whose result depends on the kind (member lookup, SRTP, overload resolution, coercions to non-tuple types) reads an undetermined kind as reference. Gated by a preview feature.

Related

Drafted with AI assistance, following the authoring-rfcs guidance.

🤖 Generated with Claude Code

Covers fslang-suggestions #988, approved in principle, following Don
Syme's design: a tuple pattern without struct gets an undetermined
kind that later code in the same declaration can decide, and that is
reference when nothing decides it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 23:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Phase-one overload resolution does not specify whether rejected candidates’ tuple kinds are committed.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Defines preview tuple-kind inference so ordinary tuple patterns can infer reference or struct representation while preserving existing behavior.

Changes:

  • Introduces undetermined tuple kinds with declaration-level defaulting.
  • Specifies inference, overload resolution, compiled form, compatibility, and tooling behavior.
File Description
RFCs/​FS-1354-struct-tuple-pattern-inference.md Adds the FS-1354 language design.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

1. **Unification.** When two tuple types unify, or one must subsume the other, their kinds unify. An undetermined kind takes the other kind; two undetermined kinds become one. Struct against reference gives today's error there (FS0001, or FS0193 at a subsumption).
2. **Constraints.** `'T : struct`, `'T : unmanaged`, `'T : (new: unit -> 'T)`, and a coercion or subtype constraint to `System.ValueType`, decide struct. `'T : not struct` decides reference.
3. **Observation.** Any other step whose result depends on the kind first makes an undetermined kind reference. A test whether two tuple types could unify does not depend on it. Such steps include member lookup (`v.Item1`), an SRTP constraint whose support is the tuple type, the argument count of an overloaded method used as a function value (`v |> Math.Max`), the `op_Implicit` search (FS-1093) between a tuple type and a non-tuple type, coercions, type tests and subtype constraints to a non-tuple type that is not a type variable (`:> obj`, `:?`, `#I`, a method parameter of such a type), and a use of a function value whose tuple domain has a non-sealed element type that is not a type variable (§14.4.3). Condensation (§14.6.8) reads an undetermined tuple domain as reference without deciding it.
4. **Overload resolution.** Phase 1 reads every undetermined kind in the call as reference: in argument and parameter types and, where resolution compares return types (`op_Explicit`, `op_Implicit`, `[<AllowOverloadOnReturnType>]` in preview, unfilled out arguments), in return types. If a candidate applies, these kinds become reference. Otherwise, phase 2 resolves again with the kinds undetermined, and rules 1 to 3 apply to the selected candidate. If phase 2 also fails, the phase 1 error is reported. The same two phases resolve SRTP member constraints. Lambda argument inference from candidates filters with phase 1, then phase 2 if none remains; this filtering decides no kinds. With one candidate and no return-type comparison, rules 1 to 3 apply.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants