Skip to content

Commit 3c3a388

Browse files
authored
Fix race in graph checking of type extensions (#19062)
1 parent a7533f7 commit 3c3a388

File tree

11 files changed

+111
-26
lines changed

11 files changed

+111
-26
lines changed

docs/release-notes/.FSharp.Compiler.Service/11.0.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Type relations cache: handle potentially "infinite" types ([PR #19010](https://github.com/dotnet/fsharp/pull/19010))
1010
* Disallow recursive structs with lifted type parameters ([Issue #18993](https://github.com/dotnet/fsharp/issues/18993), [PR #19031](https://github.com/dotnet/fsharp/pull/19031))
1111
* Fix units-of-measure changes not invalidating incremental builds. ([Issue #19049](https://github.com/dotnet/fsharp/issues/19049))
12+
* Fix race in graph checking of type extensions. ([PR #19062](https://github.com/dotnet/fsharp/pull/19062))
1213
* Type relations cache: handle unsolved type variables ([Issue #19037](https://github.com/dotnet/fsharp/issues/19037)) ([PR #19040](https://github.com/dotnet/fsharp/pull/19040))
1314

1415
### Added

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4217,18 +4217,7 @@ module TcDeclarations =
42174217
let resInfo = TypeNameResolutionStaticArgsInfo.FromTyArgs synTypars.Length
42184218
let tcref =
42194219
match ResolveTypeLongIdent cenv.tcSink cenv.nameResolver ItemOccurrence.Binding OpenQualified envForDecls.NameEnv ad longPath resInfo PermitDirectReferenceToGeneratedType.No with
4220-
| Result res ->
4221-
// Update resolved type parameters with the names from the source.
4222-
let _, tcref, _ = res
4223-
if tcref.TyparsNoRange.Length = synTypars.Length then
4224-
(tcref.TyparsNoRange, synTypars)
4225-
||> List.zip
4226-
|> List.iter (fun (typar, SynTyparDecl.SynTyparDecl (typar = tp)) ->
4227-
let (SynTypar(ident = untypedIdent; staticReq = sr)) = tp
4228-
if typar.StaticReq = sr then
4229-
typar.SetIdent(untypedIdent)
4230-
)
4231-
4220+
| Result (_, tcref, _) ->
42324221
tcref
42334222

42344223
| Exception exn ->

src/Compiler/Checking/NicePrint.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ module PrintTypes =
724724
(sprintf "%s%s%s"
725725
(if denv.showStaticallyResolvedTyparAnnotations then prefixOfStaticReq typar.StaticReq else "'")
726726
(if denv.showInferenceTyparAnnotations then prefixOfInferenceTypar typar else "")
727-
typar.DisplayName)
727+
(typar.DeclaredName |> Option.defaultValue typar.Name))
728728
|> mkNav typar.Range
729729
|> wordL
730730

@@ -1199,12 +1199,12 @@ module PrintTypes =
11991199
let prettyArgInfos denv allTyparInst =
12001200
function
12011201
| [] -> [(denv.g.unit_ty, ValReprInfo.unnamedTopArg1)]
1202-
| infos -> infos |> List.map (map1Of2 (instType allTyparInst))
1202+
| infos -> infos |> List.map (map1Of2 (instType allTyparInst))
12031203

12041204
// Layout: type spec - class, datatype, record, abbrev
12051205
let prettyLayoutOfMemberSigCore denv memberToParentInst (typarInst, methTypars: Typars, argInfos, retTy) =
12061206
let niceMethodTypars, allTyparInst =
1207-
let methTyparNames = methTypars |> List.mapi (fun i tp -> if (PrettyTypes.NeedsPrettyTyparName tp) then sprintf "a%d" (List.length memberToParentInst + i) else tp.Name)
1207+
let methTyparNames = methTypars |> List.mapi (fun i tp -> if (PrettyTypes.NeedsPrettyTyparName tp) then sprintf "a%d" (List.length memberToParentInst + i) else tp.DeclaredName |> Option.defaultValue tp.Name )
12081208
PrettyTypes.NewPrettyTypars memberToParentInst methTypars methTyparNames
12091209

12101210
let retTy = instType allTyparInst retTy
@@ -1245,7 +1245,7 @@ module PrintTypes =
12451245
let _niceMethodTypars, typarInst =
12461246
let memberToParentInst = List.empty
12471247
let typars = argInfos |> List.choose (function TType_var (typar, _),_ -> Some typar | _ -> None)
1248-
let methTyparNames = typars |> List.mapi (fun i tp -> if (PrettyTypes.NeedsPrettyTyparName tp) then sprintf "a%d" (List.length memberToParentInst + i) else tp.Name)
1248+
let methTyparNames = typars |> List.mapi (fun i tp -> if (PrettyTypes.NeedsPrettyTyparName tp) then sprintf "a%d" (List.length memberToParentInst + i) else tp.DeclaredName |> Option.defaultValue tp.Name)
12491249
PrettyTypes.NewPrettyTypars memberToParentInst typars methTyparNames
12501250

12511251
let retTy = instType typarInst retTy

src/Compiler/Checking/SignatureConformance.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) =
143143
if check then
144144
errorR (Error(FSComp.SR.typrelSigImplNotCompatibleCompileTimeRequirementsDiffer(), m))
145145

146+
if not (PrettyTypes.NeedsPrettyTyparName implTypar) then
147+
implTypar.PreserveDeclaredName()
148+
146149
// Adjust the actual type parameter name to look like the signature
147150
implTypar.SetIdent (mkSynId implTypar.Range sigTypar.Id.idText)
148151

src/Compiler/TypedTree/TypedTree.fs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,6 +2260,9 @@ type TyparOptionalData =
22602260

22612261
/// Set to true if the typar is contravariant, i.e. declared as <in T> in C#
22622262
mutable typar_is_contravariant: bool
2263+
2264+
/// The declared name of the type parameter.
2265+
mutable typar_declared_name: string option
22632266
}
22642267

22652268
[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]
@@ -2361,10 +2364,10 @@ type Typar =
23612364
member x.SetAttribs attribs =
23622365
match attribs, x.typar_opt_data with
23632366
| [], None -> ()
2364-
| [], Some { typar_il_name = None; typar_xmldoc = doc; typar_constraints = []; typar_is_contravariant = false } when doc.IsEmpty ->
2367+
| [], Some { typar_il_name = None; typar_xmldoc = doc; typar_constraints = []; typar_is_contravariant = false; typar_declared_name = None } when doc.IsEmpty ->
23652368
x.typar_opt_data <- None
23662369
| _, Some optData -> optData.typar_attribs <- attribs
2367-
| _ -> x.typar_opt_data <- Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = attribs; typar_is_contravariant = false }
2370+
| _ -> x.typar_opt_data <- Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = attribs; typar_is_contravariant = false; typar_declared_name = None }
23682371

23692372
/// Get the XML documentation for the type parameter
23702373
member x.XmlDoc =
@@ -2382,7 +2385,20 @@ type Typar =
23822385
member x.SetILName il_name =
23832386
match x.typar_opt_data with
23842387
| Some optData -> optData.typar_il_name <- il_name
2385-
| _ -> x.typar_opt_data <- Some { typar_il_name = il_name; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = []; typar_is_contravariant = false }
2388+
| _ -> x.typar_opt_data <- Some { typar_il_name = il_name; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = []; typar_is_contravariant = false; typar_declared_name = None}
2389+
2390+
/// Get the declared name of the type parameter
2391+
member x.DeclaredName =
2392+
match x.typar_opt_data with
2393+
| Some optData -> optData.typar_declared_name
2394+
| _ -> None
2395+
2396+
/// Save the name as the declared name of the type parameter if it is not already set
2397+
member x.PreserveDeclaredName() =
2398+
match x.typar_opt_data with
2399+
| Some optData when optData.typar_declared_name = None -> optData.typar_declared_name <- Some x.Name
2400+
| None -> x.typar_opt_data <- Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = []; typar_is_contravariant = false; typar_declared_name = Some x.Name }
2401+
| _ -> ()
23862402

23872403
/// Indicates the display name of a type variable
23882404
member x.DisplayName = if x.Name = "?" then "?"+string x.Stamp else x.Name
@@ -2391,17 +2407,17 @@ type Typar =
23912407
member x.SetConstraints cs =
23922408
match cs, x.typar_opt_data with
23932409
| [], None -> ()
2394-
| [], Some { typar_il_name = None; typar_xmldoc = doc; typar_attribs = [];typar_is_contravariant = false } when doc.IsEmpty ->
2410+
| [], Some { typar_il_name = None; typar_xmldoc = doc; typar_attribs = [];typar_is_contravariant = false; typar_declared_name = None } when doc.IsEmpty ->
23952411
x.typar_opt_data <- None
23962412
| _, Some optData -> optData.typar_constraints <- cs
2397-
| _ -> x.typar_opt_data <- Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = cs; typar_attribs = []; typar_is_contravariant = false }
2413+
| _ -> x.typar_opt_data <- Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = cs; typar_attribs = []; typar_is_contravariant = false; typar_declared_name = None }
23982414

23992415
/// Marks the typar as being contravariant
24002416
member x.MarkAsContravariant() =
24012417
match x.typar_opt_data with
24022418
| Some optData -> optData.typar_is_contravariant <- true
24032419
| _ ->
2404-
x.typar_opt_data <- Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = []; typar_is_contravariant = true }
2420+
x.typar_opt_data <- Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = []; typar_is_contravariant = true; typar_declared_name = None }
24052421

24062422
/// Creates a type variable that contains empty data, and is not yet linked. Only used during unpickling of F# metadata.
24072423
static member NewUnlinked() : Typar =
@@ -2423,7 +2439,7 @@ type Typar =
24232439
x.typar_solution <- tg.typar_solution
24242440
match tg.typar_opt_data with
24252441
| Some tg ->
2426-
let optData = { typar_il_name = tg.typar_il_name; typar_xmldoc = tg.typar_xmldoc; typar_constraints = tg.typar_constraints; typar_attribs = tg.typar_attribs; typar_is_contravariant = tg.typar_is_contravariant }
2442+
let optData = { typar_il_name = tg.typar_il_name; typar_xmldoc = tg.typar_xmldoc; typar_constraints = tg.typar_constraints; typar_attribs = tg.typar_attribs; typar_is_contravariant = tg.typar_is_contravariant; typar_declared_name = tg.typar_declared_name }
24272443
x.typar_opt_data <- Some optData
24282444
| None -> ()
24292445

@@ -6183,7 +6199,7 @@ type Construct() =
61836199
typar_opt_data =
61846200
match attribs with
61856201
| [] -> None
6186-
| _ -> Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = attribs; typar_is_contravariant = false } }
6202+
| _ -> Some { typar_il_name = None; typar_xmldoc = XmlDoc.Empty; typar_constraints = []; typar_attribs = attribs; typar_is_contravariant = false; typar_declared_name = None } }
61876203

61886204
/// Create a new type parameter node for a declared type parameter
61896205
static member NewRigidTypar nm m =

src/Compiler/TypedTree/TypedTree.fsi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,9 @@ type TyparOptionalData =
14941494

14951495
/// Set to true if the typar is contravariant, i.e. declared as <in T> in C#
14961496
mutable typar_is_contravariant: bool
1497+
1498+
/// The declared name of the type parameter.
1499+
mutable typar_declared_name: string option
14971500
}
14981501

14991502
override ToString: unit -> string
@@ -1562,6 +1565,9 @@ type Typar =
15621565
/// Set the IL name of the type parameter
15631566
member SetILName: il_name: string option -> unit
15641567

1568+
/// Saves the name as the declared name of the type parameter if it is not already set.
1569+
member PreserveDeclaredName: unit -> unit
1570+
15651571
/// Sets the identifier associated with a type variable
15661572
member SetIdent: id: Ident -> unit
15671573

@@ -1594,6 +1600,9 @@ type Typar =
15941600
[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]
15951601
member DebugText: string
15961602

1603+
/// Gets the declared name of the type parameter.
1604+
member DeclaredName: string option
1605+
15971606
/// Indicates the display name of a type variable
15981607
member DisplayName: string
15991608

src/Compiler/TypedTree/TypedTreeBasics.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ let mkTyparTy (tp:Typar) =
197197
// For fresh type variables clear the StaticReq when copying because the requirement will be re-established through the
198198
// process of type inference.
199199
let copyTypar clearStaticReq (tp: Typar) =
200-
let optData = tp.typar_opt_data |> Option.map (fun tg -> { typar_il_name = tg.typar_il_name; typar_xmldoc = tg.typar_xmldoc; typar_constraints = tg.typar_constraints; typar_attribs = tg.typar_attribs; typar_is_contravariant = tg.typar_is_contravariant })
200+
let optData = tp.typar_opt_data |> Option.map (fun tg -> { typar_il_name = tg.typar_il_name; typar_xmldoc = tg.typar_xmldoc; typar_constraints = tg.typar_constraints; typar_attribs = tg.typar_attribs; typar_is_contravariant = tg.typar_is_contravariant; typar_declared_name = tg.typar_declared_name })
201201
let flags = if clearStaticReq then tp.typar_flags.WithStaticReq(TyparStaticReq.None) else tp.typar_flags
202202
Typar.New { typar_id = tp.typar_id
203203
typar_flags = flags

src/Compiler/TypedTree/TypedTreePickle.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2406,6 +2406,7 @@ let u_tyar_spec_data st =
24062406
typar_constraints = e
24072407
typar_attribs = c
24082408
typar_is_contravariant = false
2409+
typar_declared_name = None
24092410
}
24102411
}
24112412

tests/FSharp.Compiler.ComponentTests/Signatures/TypeTests.fs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,38 @@ let foo = Foo()
308308
| :? FSharpMemberOrFunctionOrValue as mfv ->
309309
Assert.False(mfv.IsUnionCaseTester, "IsUnionCaseTester returned true")
310310
Assert.True(mfv.IsConstructor)
311-
| _ -> failwith "Expected FSharpMemberOrFunctionOrValue"
311+
| _ -> failwith "Expected FSharpMemberOrFunctionOrValue"
312+
313+
314+
315+
[<Fact>]
316+
let ``Type extension type parameters do not overwrite the type being extended`` () =
317+
let one =
318+
FSharpWithFileName "One.fs" """
319+
module One
320+
321+
type GenericType<'ActualType> = {
322+
Value: 'ActualType
323+
}
324+
"""
325+
326+
let two =
327+
FsSourceWithFileName "Two.fs" """
328+
module Two
329+
type One.GenericType<'U> with
330+
member x.Print () = printfn "%A" x.Value
331+
"""
332+
333+
let three =
334+
FsSourceWithFileName "Three.fs" """
335+
module Three
336+
337+
open One
338+
339+
type GenericType<'X> with
340+
member _.Nothing() = ignore ()
341+
"""
342+
343+
one |> withAdditionalSourceFiles [ two; three ]
344+
|> compile
345+
|> verifyILContains [ ".Print<ActualType>" ]

tests/FSharp.Compiler.ComponentTests/TypeChecks/TyparNameTests.fs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,32 @@ let array2D (rows: seq<#seq<'T>>) : 'T[,] = failwith "todo"
141141
getGenericParametersNamesFor signatureFile "A" "array2D" implementationFile
142142

143143
Assert.Equal<string array>([| "a"; "T" |], names)
144+
145+
[<Fact>]
146+
let ``Type extension type parameters are preserved as declared name`` () =
147+
let one =
148+
FSharpWithFileName "One.fs" """
149+
module One
150+
151+
type GenericType<'ActualType> = {
152+
Value: 'ActualType
153+
}
154+
"""
155+
|> withFileName "One.fs"
156+
157+
let two =
158+
FsSourceWithFileName "Two.fs" """
159+
module Two
160+
type One.GenericType<'DeclaredType> with
161+
member x.Print () = printfn "%A" x.Value
162+
"""
163+
164+
let result =
165+
one |> withAdditionalSourceFile two
166+
|> typecheckProject false CompilerAssertHelpers.UseTransparentCompiler
167+
168+
let typar =
169+
result.AssemblySignature.Entities[0].MembersFunctionsAndValues[0].GenericParameters[0].TypeParameter
170+
171+
Assert.Equal(Some "DeclaredType", typar.DeclaredName)
172+
Assert.Equal("ActualType", typar.Name)

0 commit comments

Comments
 (0)