dotnet / dotnet/fsharp

Signature generation drops parentheses around nested tuple argument when a sibling argument is named

Open Beginner friendly
#20,397 0 comments 1 reaction 0 assignees View on GitHub
Area-Compiler-SigFileGen Bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.