RFC FS-1354 - Struct-ness inference for tuple patterns - #857
Open
xperiandri wants to merge 2 commits into
Open
xperiandri wants to merge 2 commits into
xperiandri wants to merge 2 commits into
Conversation
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>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Phase-one overload resolution does not specify whether rejected candidates’ tuple kinds are committed.
Review effort: Balanced
Findings: 1
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. |
dsyme
approved these changes
Oct 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Click “Files changed” → “⋯” → “View file” for the rendered RFC.
Summary
A tuple pattern without
structgets 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.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
fst/sndoverloads) and RFC FS-1344: Deconstruct method support in tuple patterns #840 (FS-1344Deconstructpatterns)Drafted with AI assistance, following the authoring-rfcs guidance.
🤖 Generated with Claude Code