Skip to content

Commit 24eef2c

Browse files
authored
Add typars in binding when there is an order mismatch. (#15366)
1 parent 4c0eb4d commit 24eef2c

File tree

8 files changed

+89
-14
lines changed

8 files changed

+89
-14
lines changed

src/Compiler/Checking/CheckPatterns.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ and TcSimplePat optionalArgsOK checkConstraints (cenv: cenv) ty env patEnv p =
105105
| _ -> UnifyTypes cenv env m ty ctyR
106106

107107
let patEnvR = TcPatLinearEnv(tpenv, names, takenNames)
108+
109+
// Ensure the untyped typar name sticks
110+
match cty, ty with
111+
| SynType.Var(typar = SynTypar(ident = untypedIdent)), TType_var(typar = typedTp) -> typedTp.SetIdent(untypedIdent)
112+
| _ -> ()
113+
108114
TcSimplePat optionalArgsOK checkConstraints cenv ty env patEnvR p
109115

110116
| SynSimplePat.Attrib (p, _, _) ->

src/Compiler/Checking/NicePrint.fs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,14 +1259,15 @@ module PrintTastMemberOrVals =
12591259
else
12601260
nameL
12611261

1262-
let layoutMemberName (denv: DisplayEnv) (vref: ValRef) niceMethodTypars tagFunction name =
1262+
let layoutMemberName (denv: DisplayEnv) (vref: ValRef) niceMethodTypars argInfos tagFunction name =
12631263
let nameL = ConvertValLogicalNameToDisplayLayout vref.IsBaseVal (tagFunction >> mkNav vref.DefinitionRange >> wordL) name
12641264
let nameL =
12651265
if denv.showMemberContainers then
12661266
layoutTyconRef denv vref.MemberApparentEntity ^^ SepL.dot ^^ nameL
12671267
else
12681268
nameL
1269-
let nameL = if denv.showTyparBinding then layoutTyparDecls denv nameL true niceMethodTypars else nameL
1269+
let typarOrderMismatch = isTyparOrderMismatch niceMethodTypars argInfos
1270+
let nameL = if denv.showTyparBinding || typarOrderMismatch then layoutTyparDecls denv nameL true niceMethodTypars else nameL
12701271
let nameL = layoutAccessibility denv vref.Accessibility nameL
12711272
nameL
12721273

@@ -1289,7 +1290,7 @@ module PrintTastMemberOrVals =
12891290
let resL =
12901291
if short then tauL
12911292
else
1292-
let nameL = layoutMemberName denv vref niceMethodTypars tagMember vref.DisplayNameCoreMangled
1293+
let nameL = layoutMemberName denv vref niceMethodTypars argInfos tagMember vref.DisplayNameCoreMangled
12931294
let nameL = if short then nameL else mkInlineL denv vref.Deref nameL
12941295
stat --- ((nameL |> addColonL) ^^ tauL)
12951296
prettyTyparInst, resL
@@ -1311,7 +1312,7 @@ module PrintTastMemberOrVals =
13111312
if isNil argInfos then
13121313
// use error recovery because intellisense on an incomplete file will show this
13131314
errorR(Error(FSComp.SR.tastInvalidFormForPropertyGetter(), vref.Id.idRange))
1314-
let nameL = layoutMemberName denv vref [] tagProperty vref.DisplayNameCoreMangled
1315+
let nameL = layoutMemberName denv vref [] argInfos tagProperty vref.DisplayNameCoreMangled
13151316
let resL =
13161317
if short then nameL --- (WordL.keywordWith ^^ WordL.keywordGet)
13171318
else stat --- nameL --- (WordL.keywordWith ^^ WordL.keywordGet)
@@ -1327,25 +1328,26 @@ module PrintTastMemberOrVals =
13271328
if isNil argInfos then tauL
13281329
else tauL --- (WordL.keywordWith ^^ WordL.keywordGet)
13291330
else
1330-
let nameL = layoutMemberName denv vref niceMethodTypars tagProperty vref.DisplayNameCoreMangled
1331+
let nameL = layoutMemberName denv vref niceMethodTypars argInfos tagProperty vref.DisplayNameCoreMangled
13311332
stat --- ((nameL |> addColonL) ^^ (if isNil argInfos then tauL else tauL --- (WordL.keywordWith ^^ WordL.keywordGet)))
13321333
prettyTyparInst, resL
13331334

13341335
| SynMemberKind.PropertySet ->
13351336
if argInfos.Length <> 1 || isNil argInfos.Head then
13361337
// use error recovery because intellisense on an incomplete file will show this
13371338
errorR(Error(FSComp.SR.tastInvalidFormForPropertySetter(), vref.Id.idRange))
1338-
let nameL = layoutMemberName denv vref [] tagProperty vref.DisplayNameCoreMangled
1339+
let nameL = layoutMemberName denv vref [] argInfos tagProperty vref.DisplayNameCoreMangled
13391340
let resL = stat --- nameL --- (WordL.keywordWith ^^ WordL.keywordSet)
13401341
emptyTyparInst, resL
13411342
else
1343+
let curriedArgInfos = argInfos
13421344
let argInfos, valueInfo = List.frontAndBack argInfos.Head
13431345
let prettyTyparInst, niceMethodTypars, tauL = prettyLayoutOfMemberType denv vref typarInst (if isNil argInfos then [] else [argInfos]) (fst valueInfo)
13441346
let resL =
13451347
if short then
13461348
(tauL --- (WordL.keywordWith ^^ WordL.keywordSet))
13471349
else
1348-
let nameL = layoutMemberName denv vref niceMethodTypars tagProperty vref.DisplayNameCoreMangled
1350+
let nameL = layoutMemberName denv vref niceMethodTypars curriedArgInfos tagProperty vref.DisplayNameCoreMangled
13491351
stat --- ((nameL |> addColonL) ^^ (tauL --- (WordL.keywordWith ^^ WordL.keywordSet)))
13501352
prettyTyparInst, resL
13511353

@@ -1412,9 +1414,11 @@ module PrintTastMemberOrVals =
14121414
let nameL = mkInlineL denv v nameL
14131415

14141416
let isOverGeneric = List.length (Zset.elements (freeInType CollectTyparsNoCaching tau).FreeTypars) < List.length tps // Bug: 1143
1415-
let isTyFunction = v.IsTypeFunction // Bug: 1143, and innerpoly tests
1417+
let isTyFunction = v.IsTypeFunction // Bug: 1143, and innerpoly tests
1418+
let typarOrderMismatch = isTyparOrderMismatch tps argInfos
1419+
14161420
let typarBindingsL =
1417-
if isTyFunction || isOverGeneric || denv.showTyparBinding then
1421+
if isTyFunction || isOverGeneric || denv.showTyparBinding || typarOrderMismatch then
14181422
layoutTyparDecls denv nameL true tps
14191423
else nameL
14201424
let valAndTypeL = (WordL.keywordVal ^^ (typarBindingsL |> addColonL)) --- layoutTopType denv env argInfos retTy cxs

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10633,3 +10633,32 @@ let updateSeqTypeIsPrefix (fsharpCoreMSpec: ModuleOrNamespace) =
1063310633
)
1063410634
)
1063510635
)
10636+
10637+
let isTyparOrderMismatch (tps: Typars) (argInfos: CurriedArgInfos) =
10638+
let rec getTyparName (ty: TType) : string list =
10639+
match ty with
10640+
| TType_var (typar = tp) ->
10641+
if tp.Id.idText <> unassignedTyparName then
10642+
[ tp.Id.idText ]
10643+
else
10644+
match tp.Solution with
10645+
| None -> []
10646+
| Some solutionType -> getTyparName solutionType
10647+
| TType_fun(domainType, rangeType, _) -> [ yield! getTyparName domainType; yield! getTyparName rangeType ]
10648+
| TType_anon(tys = ti)
10649+
| TType_app (typeInstantiation = ti)
10650+
| TType_tuple (elementTypes = ti) -> List.collect getTyparName ti
10651+
| _ -> []
10652+
10653+
let typarNamesInArguments =
10654+
argInfos
10655+
|> List.collect (fun argInfos ->
10656+
argInfos
10657+
|> List.collect (fun (ty, _) -> getTyparName ty))
10658+
|> List.distinct
10659+
10660+
let typarNamesInDefinition =
10661+
tps |> List.map (fun (tp: Typar) -> tp.Id.idText) |> List.distinct
10662+
10663+
typarNamesInArguments.Length = typarNamesInDefinition.Length
10664+
&& typarNamesInArguments <> typarNamesInDefinition

src/Compiler/TypedTree/TypedTreeOps.fsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,3 +2705,8 @@ val serializeEntity: path: string -> entity: Entity -> unit
27052705
/// Updates the IsPrefixDisplay to false for the Microsoft.FSharp.Collections.seq`1 entity
27062706
/// Meant to be called with the FSharp.Core module spec right after it was unpickled.
27072707
val updateSeqTypeIsPrefix: fsharpCoreMSpec: ModuleOrNamespace -> unit
2708+
2709+
/// Check if the order of defined typars is different from the order of used typars in the curried arguments.
2710+
/// If this is the case, a generated signature would require explicit typars.
2711+
/// See https://github.com/dotnet/fsharp/issues/15175
2712+
val isTyparOrderMismatch: Typars -> CurriedArgInfos -> bool
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module OrderMatters
2+
3+
let f<'a, 'b> (x: 'b) (y: 'a) = ()
4+
5+
type T() =
6+
member this.i<'a, 'b> (x: 'b) (y: 'a) = printfn "%A %A" x y
7+
8+
// compound types
9+
let h1<'a, 'b> (x: 'b * 'a) = ()
10+
let h2<'a, 'b> (x: 'b -> 'a) = ()
11+
let h3<'a, 'b> (x: {| F1: 'b; F2: 'a|}) = ()
12+
let h4<'a, 'b> (x: seq<'b> * array<int * 'a>) = ()
13+
14+
// Avoid duplicate names
15+
let z<'a, 'z> (z1: 'z) (z2: 'z) (z3: 'a) : 'z = z1
16+
17+
type IMonad<'a> =
18+
interface
19+
// Hash constraint leads to another type parameter
20+
abstract bind : #IMonad<'a> -> ('a -> #IMonad<'b>) -> IMonad<'b>
21+
end
22+
23+
open System.Runtime.InteropServices
24+
25+
type A<'zzz>() =
26+
// Process the solution of typar as well
27+
static member Foo(argA2: 'a, argB2: 'a -> 'b, argC2: 'b -> 'c, argD: 'c -> 'd, [<Optional>] argZ2: 'zzz) : 'd = argD (argC2( argB2 argA2))
28+
29+
type C<'a>() =
30+
// The explicit parameters are required here as well.
31+
static member SM5<'b,'c>(y:'a,z:'b) = 2

tests/fsharp/typecheck/sigs/neg20.bsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ neg20.fs(157,28,157,29): typecheck error FS0495: The member or object constructo
131131

132132
neg20.fs(158,13,158,36): typecheck error FS0502: The member or object constructor 'SM4' takes 1 type argument(s) but is here given 2. The required signature is 'static member C.SM4: y: 'a * z: 'b -> int'.
133133

134-
neg20.fs(159,13,159,32): typecheck error FS0502: The member or object constructor 'SM5' takes 2 type argument(s) but is here given 1. The required signature is 'static member C.SM5: y: 'a * z: 'b -> int'.
134+
neg20.fs(159,13,159,32): typecheck error FS0502: The member or object constructor 'SM5' takes 2 type argument(s) but is here given 1. The required signature is 'static member C.SM5<'b,'c> : y: 'a * z: 'b -> int'.
135135

136136
neg20.fs(162,13,162,24): typecheck error FS0502: The member or object constructor 'M1' takes 0 type argument(s) but is here given 1. The required signature is 'member C.M1: unit -> int'.
137137

@@ -143,7 +143,7 @@ neg20.fs(165,27,165,28): typecheck error FS0495: The member or object constructo
143143

144144
neg20.fs(166,13,166,35): typecheck error FS0502: The member or object constructor 'M4' takes 1 type argument(s) but is here given 2. The required signature is 'member C.M4: y: 'a * z: 'b -> int'.
145145

146-
neg20.fs(167,13,167,31): typecheck error FS0502: The member or object constructor 'M5' takes 2 type argument(s) but is here given 1. The required signature is 'member C.M5: y: 'a * z: 'b -> int'.
146+
neg20.fs(167,13,167,31): typecheck error FS0502: The member or object constructor 'M5' takes 2 type argument(s) but is here given 1. The required signature is 'member C.M5<'b,'c> : y: 'a * z: 'b -> int'.
147147

148148
neg20.fs(182,14,182,31): typecheck error FS0041: No overloads match for method 'M'.
149149

tests/fsharp/typecheck/sigs/version50/neg20.bsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ neg20.fs(157,28,157,29): typecheck error FS0495: The member or object constructo
181181

182182
neg20.fs(158,13,158,36): typecheck error FS0502: The member or object constructor 'SM4' takes 1 type argument(s) but is here given 2. The required signature is 'static member C.SM4: y: 'a * z: 'b -> int'.
183183

184-
neg20.fs(159,13,159,32): typecheck error FS0502: The member or object constructor 'SM5' takes 2 type argument(s) but is here given 1. The required signature is 'static member C.SM5: y: 'a * z: 'b -> int'.
184+
neg20.fs(159,13,159,32): typecheck error FS0502: The member or object constructor 'SM5' takes 2 type argument(s) but is here given 1. The required signature is 'static member C.SM5<'b,'c> : y: 'a * z: 'b -> int'.
185185

186186
neg20.fs(162,13,162,24): typecheck error FS0502: The member or object constructor 'M1' takes 0 type argument(s) but is here given 1. The required signature is 'member C.M1: unit -> int'.
187187

@@ -193,7 +193,7 @@ neg20.fs(165,27,165,28): typecheck error FS0495: The member or object constructo
193193

194194
neg20.fs(166,13,166,35): typecheck error FS0502: The member or object constructor 'M4' takes 1 type argument(s) but is here given 2. The required signature is 'member C.M4: y: 'a * z: 'b -> int'.
195195

196-
neg20.fs(167,13,167,31): typecheck error FS0502: The member or object constructor 'M5' takes 2 type argument(s) but is here given 1. The required signature is 'member C.M5: y: 'a * z: 'b -> int'.
196+
neg20.fs(167,13,167,31): typecheck error FS0502: The member or object constructor 'M5' takes 2 type argument(s) but is here given 1. The required signature is 'member C.M5<'b,'c> : y: 'a * z: 'b -> int'.
197197

198198
neg20.fs(182,14,182,31): typecheck error FS0041: No overloads match for method 'M'.
199199

vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ Full name: Microsoft.FSharp.Core.Operators.(||>)
280280
'U is float"
281281
mkDesc
282282
"let res4 = (1.0,[1]) ||> List.fold"
283-
"val fold: folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State
283+
"val fold<'T,'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State
284284
Full name: Microsoft.FSharp.Collections.List.fold
285285
'T is int
286286
'State is float"

0 commit comments

Comments
 (0)