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
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 (7ea5992) and on FCS 43.11.303 through 43.12.400:
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
| 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
match stripTyEqns g ty with
(g is already bound at the top of layoutArgInfo.) With that one-line change, fsc --sig on main prints
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 to FCS 43.12.
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) * stringcomes out as the flat 3-tupleint * float * string. The printed type is a different type.Repro
fsc --sig:X.fsi -a X.fson currentmain(7ea5992) and on FCS 43.11.303 through 43.12.400:FSharpMemberOrFunctionOrValue.CurriedParameterGroupsis identical foraandbapart from the namez, so only the printing differs.Cause
#18842 changed the unnamed-argument branch of
PrintTypes.layoutArgInfoinsrc/Compiler/Checking/NicePrint.fstoThe 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_ -> 4arm and the tuple case'sbracketIfL (prec <= 2)never fires.bis fine because a fully unnamed group is laid out as a single type, andeis fine because the named branch still passes precedence 2 unconditionally.Fix
(
gis already bound at the top oflayoutArgInfo.) With that one-line change,fsc --sigonmainprintsHappy to open a PR with the change and a
--sigroundtrip test.Found while upgrading Telplin to FCS 43.12.