Skip to content

Commit 4a0fc41

Browse files
T-GroCopilot
andcommitted
Fix CI failures from signature roundtrip changes
- NicePrint: null-safe typar name handling in layoutTyparRef to avoid NRE when rendering anonymous typars during overload resolution. - NicePrint: deduplicate SRTP constraints in layoutNonMemberVal so they appear only in typar decl brackets, not duplicated in the postfix 'when' clause. - PrettyNaming: revert active-pattern case backtick escaping from ConvertValLogicalNameToDisplayNameCore (which is also used by tryParseActivePatternName parsing path). Apply backtick escaping only at final display layer via new escapeActivePatternCaseNames helper in ConvertValLogicalNameToDisplayName and ConvertValLogicalNameToDisplayLayout. - Update IWSAMsAndSRTPs signature test expectations to match new val inline f<^T ...> : ... format. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 53b9e61 commit 4a0fc41

File tree

3 files changed

+45
-26
lines changed

3 files changed

+45
-26
lines changed

src/Compiler/Checking/NicePrint.fs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,12 @@ module PrintTypes =
730730
| _, _ -> squareAngleL (sepListL RightL.semicolon ((match kind with TyparKind.Type -> [] | TyparKind.Measure -> [wordL (tagText "Measure")]) @ List.map (layoutAttrib denv) attrs)) ^^ restL
731731

732732
and layoutTyparRef denv (typar: Typar) =
733-
let name = NormalizeIdentifierBackticks (typar.DeclaredName |> Option.defaultValue typar.Name)
733+
let rawName =
734+
match typar.DeclaredName with
735+
| Some n when not (isNull (box n)) -> n
736+
| _ -> typar.Name
737+
let name =
738+
if isNull (box rawName) then "" else NormalizeIdentifierBackticks rawName
734739
tagTypeParameter
735740
(sprintf "%s%s%s"
736741
(if denv.showStaticallyResolvedTyparAnnotations then prefixOfStaticReq typar.StaticReq else "'")
@@ -1502,6 +1507,15 @@ module PrintTastMemberOrVals =
15021507
let layoutNonMemberVal denv (tps, v: Val, tau, cxs) =
15031508
let env = SimplifyTypes.CollectInfo true [tau] cxs
15041509
let cxs = env.postfixConstraints
1510+
let hasStaticallyResolvedTypars =
1511+
tps |> List.exists (fun (tp: Typar) -> tp.StaticReq = TyparStaticReq.HeadType) &&
1512+
not (IsLogicalOpName v.LogicalName)
1513+
// When SRTP typars are shown on explicit type param declarations, exclude
1514+
// their constraints from the postfix to avoid duplicated "when" clauses.
1515+
let cxs =
1516+
if hasStaticallyResolvedTypars then
1517+
cxs |> List.filter (fun (tp, _) -> tp.StaticReq <> TyparStaticReq.HeadType)
1518+
else cxs
15051519
let valReprInfo = arityOfValForDisplay v
15061520
let argInfos, retTy = GetTopTauTypeInFSharpForm denv.g valReprInfo.ArgInfos tau v.Range
15071521
let nameL =
@@ -1535,9 +1549,6 @@ module PrintTastMemberOrVals =
15351549
let isTyFunction = v.IsTypeFunction // Bug: 1143, and innerpoly tests
15361550
let typarOrderMismatch = isTyparOrderMismatch tps argInfos
15371551

1538-
let hasStaticallyResolvedTypars =
1539-
tps |> List.exists (fun tp -> tp.StaticReq = TyparStaticReq.HeadType) &&
1540-
not (IsLogicalOpName v.LogicalName)
15411552
let typarBindingsL =
15421553
if isTyFunction || isOverGeneric || denv.showTyparBinding || typarOrderMismatch || hasStaticallyResolvedTypars then
15431554
layoutTyparDecls denv nameL true tps

src/Compiler/SyntaxTree/PrettyNaming.fs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -501,24 +501,29 @@ let ConvertValLogicalNameToDisplayNameCore opName =
501501
match standardOpsDecompile.TryGetValue opName with
502502
| true, res -> res
503503
| false, _ ->
504-
if IsActivePatternName opName then
505-
// Active pattern case names may need backtick escaping (e.g. |``A B``|)
506-
let inner = opName.[1 .. opName.Length - 2] // strip outer | |
507-
let cases = inner.Split('|')
508-
509-
let escapedCases =
510-
cases
511-
|> Array.map (fun c ->
512-
if c = "_" then c
513-
elif not (IsIdentifierName c) then "``" + c + "``"
514-
else c)
515-
516-
"|" + (escapedCases |> String.concat "|") + "|"
517-
elif IsPossibleOpName opName then
504+
if IsPossibleOpName opName then
518505
decompileCustomOpName opName
519506
else
520507
opName
521508

509+
/// For active pattern names, optionally escape individual case names that are not valid F# identifiers
510+
/// with backticks (e.g. |A B| -> |``A B``|). Leaves well-formed identifier cases alone.
511+
let private escapeActivePatternCaseNames (opName: string) =
512+
if IsActivePatternName opName then
513+
let inner = opName.[1 .. opName.Length - 2]
514+
let cases = inner.Split('|')
515+
516+
let escapedCases =
517+
cases
518+
|> Array.map (fun c ->
519+
if c = "_" then c
520+
elif not (IsIdentifierName c) then "``" + c + "``"
521+
else c)
522+
523+
"|" + (escapedCases |> String.concat "|") + "|"
524+
else
525+
opName
526+
522527
let DoesIdentifierNeedBackticks (name: string) : bool =
523528
not (IsUnencodedOpName name)
524529
&& not (IsIdentifierName name)
@@ -558,8 +563,9 @@ let ConvertValLogicalNameToDisplayName isBaseVal name =
558563
// Add parentheses for multiply-like symbols, with spacing to avoid confusion with comments
559564
elif nm <> "*" && (nm.StartsWithOrdinal "*" || nm.EndsWithOrdinal "*") then
560565
"( " + nm + " )"
561-
// Add parentheses for other symbols, no spacing
566+
// Add parentheses for other symbols, no spacing; escape active pattern case names that aren't identifiers
562567
else
568+
let nm = escapeActivePatternCaseNames nm
563569
"(" + nm + ")"
564570
else
565571
ConvertLogicalNameToDisplayName name
@@ -585,6 +591,8 @@ let ConvertValLogicalNameToDisplayLayout isBaseVal nonOpLayout name =
585591
^^ wordL (TaggedText.tagOperator nm)
586592
^^ wordL (TaggedText.tagPunctuation ")")
587593
else
594+
let nm = escapeActivePatternCaseNames nm
595+
588596
leftL (TaggedText.tagPunctuation "(")
589597
^^ wordL (TaggedText.tagOperator nm)
590598
^^ rightL (TaggedText.tagPunctuation ")")

tests/FSharp.Compiler.ComponentTests/Conformance/Types/TypeConstraints/IWSAMsAndSRTPs/IWSAMsAndSRTPsTests.fs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,24 +80,24 @@ let main _ =
8080

8181
[<Theory>]
8282
[<InlineData("let inline f0 (x: ^T) = x",
83-
"val inline f0: x: ^T -> ^T")>]
83+
"val inline f0<^T> : x: ^T -> ^T")>]
8484

8585
[<InlineData("""
8686
let inline f0 (x: ^T) = x
8787
let g0 (x: 'T) = f0 x""",
8888
"val g0: x: 'T -> 'T")>]
8989

9090
[<InlineData("let inline f1 (x: ^T) = (^T : (static member A: int) ())",
91-
"val inline f1: x: ^T -> int when ^T: (static member A: int)")>]
91+
"val inline f1<^T when ^T: (static member A: int)> : x: ^T -> int")>]
9292

9393
[<InlineData("let inline f2 (x: 'T) = ((^T or int) : (static member A: int) ())",
94-
"val inline f2: x: ^T -> int when (^T or int) : (static member A: int)")>]
94+
"val inline f2<^T when (^T or int) : (static member A: int)> : x: ^T -> int")>]
9595

9696
[<InlineData("let inline f3 (x: 'T) = ((^U or 'T) : (static member A: int) ())",
97-
"val inline f3: x: ^T -> int when (^U or ^T) : (static member A: int)")>]
97+
"val inline f3<^T,^U when (^U or ^T) : (static member A: int)> : x: ^T -> int")>]
9898

9999
[<InlineData("let inline f4 (x: 'T when 'T : (static member A: int) ) = 'T.A",
100-
"val inline f4: x: ^T -> int when ^T: (static member A: int)")>]
100+
"val inline f4<^T when ^T: (static member A: int)> : x: ^T -> int")>]
101101

102102
[<InlineData("""
103103
let inline f5 (x: ^T) = printfn "%d" x
@@ -106,11 +106,11 @@ let main _ =
106106
[<InlineData("""
107107
let inline f5 (x: ^T) = printfn "%d" x
108108
let inline h5 (x: 'T) = f5 x""",
109-
"val inline h5: x: ^T -> unit when ^T: (byte|int16|int32|int64|sbyte|uint16|uint32|uint64|nativeint|unativeint)")>]
109+
"val inline h5<^T when ^T: (byte|int16|int32|int64|sbyte|uint16|uint32|uint64|nativeint|unativeint)> : x: ^T -> unit")>]
110110
[<InlineData("""
111111
let inline uint32 (value: ^T) = (^T : (static member op_Explicit: ^T -> uint32) (value))
112112
let inline uint value = uint32 value""",
113-
"val inline uint: value: ^a -> uint32 when ^a: (static member op_Explicit: ^a -> uint32)")>]
113+
"val inline uint<^a when ^a: (static member op_Explicit: ^a -> uint32)> : value: ^a -> uint32")>]
114114

115115
[<InlineData("let checkReflexive f x y = (f x y = - f y x)",
116116
"val checkReflexive: f: ('a -> 'a -> int) -> x: 'a -> y: 'a -> bool")>]

0 commit comments

Comments
 (0)