Signature generation drops parentheses around nested tuple argument when a sibling argument is named
- Dominant language
- F#
- Stars
- 4.3k
- Forks
- 876
- Avg merge
- 4d 11h
- Merged PRs (30d)
- 131
Description
Generated signatures (`GetValSignatureText`, `GenerateSignature`, `fsc --sig`) drop the parentheses around a tuple-typed **unnamed** argument when another argument in the same curried group is **named**, so `(int * float) * string` comes out as the flat 3-tuple `int * float * string`. The printed type is a different type.
**Repro**
```fsharp
module X
let a ((x: int, y: float), z: string) = 1uy
let d ((x, y): int * float, z: string) = 1uy
let e ((p: int * float), z: string) = 1uy
let b (p: (int * float) * string) = 1uy
```
`fsc --sig:X.fsi -a X.fs` on current `main` (7ea5992e9e) and on FCS 43.11.303 through 43.12.400:
```fsharp
val a: int * float * z: string -> byte // wrong, was (int * float) * z: string in 43.9.303
val d: int * float * z: string -> byte // wrong
val e: p: (int * float) * z: string -> byte // ok, named
val b: (int * float) * string -> byte // ok, whole group unnamed
```
`FSharpMemberOrFunctionOrValue.CurriedParameterGroups` is identical for `a` and `b` apart from the name `z`, so only the printing differs.
**Cause**
#18842 changed the unnamed-argument branch of `PrintTypes.layoutArgInfo` in `src/Compiler/Checking/NicePrint.fs` to
```fsharp
| None, _, _ ->
let prec =
match ty with
| TType_tuple _ -> 2
| _ -> 4
layoutTypeWithInfoAndPrec denv env prec ty
```
The match is on the raw `ty`. For a destructured or pattern-annotated tuple parameter the argument's type is an inference variable solved to a tuple, so it falls into the `_ -> 4` arm and the tuple case's `bracketIfL (prec <= 2)` never fires. `b` is fine because a fully unnamed group is laid out as a single type, and `e` is fine because the named branch still passes precedence 2 unconditionally.
**Fix**
```fsharp
match stripTyEqns g ty with
```
(`g` is already bound at the top of `layoutArgInfo`.) With that one-line change, `fsc --sig` on `main` prints
```fsharp
val a: (int * float) * z: string -> byte
val d: (int * float) * z: string -> byte
val e: p: (int * float) * z: string -> byte
val b: (int * float) * string -> byte
```
Happy to open a PR with the change and a `--sig` roundtrip test.
Found while upgrading [Telplin](https://github.com/nojaf/telplin) to FCS 43.12.
Contributor guide
Research direction
Start in src/Compiler/Checking/NicePrint.fs at the unnamed-argument branch of PrintTypes.layoutArgInfo, then reproduce the issue with the supplied fsc --sig examples. Verify the signature preserves parentheses for a and d while keeping e and b unchanged, and add the mentioned --sig roundtrip test. Done means the generated signatures print the nested tuple type correctly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- fsharp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100