Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 4f16bfa

Browse files
TIHannosami
authored andcommitted
Fix 'open type' on generic unions and records (dotnet#9932)
* Fixed generic union type instantiation on open type * Fixed record type instantiation. Added tests for pattern matching on union and records. * Consolidating fix * remove comment * Renamed field and updated comment * Minor updates
1 parent 7f1161b commit 4f16bfa

File tree

4 files changed

+150
-53
lines changed

4 files changed

+150
-53
lines changed

src/fsharp/NameResolution.fs

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ type ArgumentContainer =
139139
// let (|A|B|) x = if x < 0 then A else B // A and B are reported as results using 'Item.ActivePatternResult'
140140
// match () with | A | B -> () // A and B are reported using 'Item.ActivePatternCase'
141141

142+
let emptyTypeInst : TypeInst = []
142143
type EnclosingTypeInst = TypeInst
143-
let emptyEnclosingTypeInst : EnclosingTypeInst = []
144+
let emptyEnclosingTypeInst : EnclosingTypeInst = emptyTypeInst
144145

145146
[<NoEquality; NoComparison; RequireQualifiedAccess>]
146147
/// Represents an item that results from name resolution
@@ -281,7 +282,7 @@ let ItemWithNoInst item = ({ Item = item; TyparInst = emptyTyparInst } : ItemWit
281282
let (|ItemWithInst|) (x: ItemWithInst) = (x.Item, x.TyparInst)
282283

283284
/// Represents a record field resolution and the information if the usage is deprecated.
284-
type FieldResolution = FieldResolution of RecdFieldRef * bool
285+
type FieldResolution = FieldResolution of RecdFieldInfo * bool
285286

286287
/// Information about an extension member held in the name resolution environment
287288
type ExtensionMember =
@@ -366,6 +367,10 @@ type NameResolutionEnv =
366367
/// Bools indicate if from a record, where no warning is given on indeterminate lookup
367368
eFieldLabels: NameMultiMap<RecdFieldRef>
368369

370+
/// Record or unions that may have type instantiations associated with them
371+
/// when record labels or union cases are used in an unqualified context.
372+
eUnqualifiedRecordOrUnionTypeInsts: TyconRefMap<TypeInst>
373+
369374
/// Tycons indexed by the various names that may be used to access them, e.g.
370375
/// "List" --> multiple TyconRef's for the various tycons accessible by this name.
371376
/// "List`1" --> TyconRef
@@ -397,6 +402,7 @@ type NameResolutionEnv =
397402
eModulesAndNamespaces = Map.empty
398403
eFullyQualifiedModulesAndNamespaces = Map.empty
399404
eFieldLabels = Map.empty
405+
eUnqualifiedRecordOrUnionTypeInsts = TyconRefMap.Empty
400406
eUnqualifiedItems = LayeredMap.Empty
401407
eUnqualifiedEnclosingTypeInsts = TyconRefMap.Empty
402408
ePatItems = Map.empty
@@ -1149,13 +1155,13 @@ and private AddTyconRefsWithEnclosingTypeInstToNameEnv bulkAddMode ownDefinition
11491155
AddTyconRefsToNameEnv bulkAddMode ownDefinition g amap ad m root nenv tcrefs
11501156

11511157
and private AddStaticPartsOfTypeToNameEnv (amap: Import.ImportMap) m nenv ty =
1152-
match tryTcrefOfAppTy amap.g ty with
1153-
| ValueSome tcref ->
1154-
AddStaticPartsOfTyconRefToNameEnv BulkAdd.Yes false amap.g amap m nenv tcref
1158+
match tryAppTy amap.g ty with
1159+
| ValueSome (tcref, tinst) ->
1160+
AddStaticPartsOfTyconRefToNameEnv BulkAdd.Yes false amap.g amap m nenv (Some tinst) tcref
11551161
| _ ->
11561162
nenv
11571163

1158-
and private AddStaticPartsOfTyconRefToNameEnv bulkAddMode ownDefinition g amap m nenv (tcref: TyconRef) =
1164+
and private AddStaticPartsOfTyconRefToNameEnv bulkAddMode ownDefinition g amap m nenv tinstOpt (tcref: TyconRef) =
11591165
let isIL = tcref.IsILTycon
11601166
let ucrefs = if isIL then [] else tcref.UnionCasesAsList |> List.map tcref.MakeNestedUnionCaseRef
11611167
let flds = if isIL then [| |] else tcref.AllFieldsArray
@@ -1194,8 +1200,19 @@ and private AddStaticPartsOfTyconRefToNameEnv bulkAddMode ownDefinition g amap m
11941200
// Union cases for patterns
11951201
AddUnionCases1 nenv.ePatItems ucrefs
11961202

1203+
let eUnqualifiedRecordOrUnionTypeInsts =
1204+
if isILOrRequiredQualifiedAccess || not (tcref.IsRecordTycon || tcref.IsUnionTycon) then
1205+
nenv.eUnqualifiedRecordOrUnionTypeInsts
1206+
else
1207+
match tinstOpt with
1208+
| None
1209+
| Some [] -> nenv.eUnqualifiedEnclosingTypeInsts
1210+
| Some tinst ->
1211+
nenv.eUnqualifiedRecordOrUnionTypeInsts.Add tcref tinst
1212+
11971213
{ nenv with
11981214
eFieldLabels = eFieldLabels
1215+
eUnqualifiedRecordOrUnionTypeInsts = eUnqualifiedRecordOrUnionTypeInsts
11991216
eUnqualifiedItems = eUnqualifiedItems
12001217
ePatItems = ePatItems
12011218
eIndexedExtensionMembers = eIndexedExtensionMembers
@@ -1238,7 +1255,7 @@ and private AddPartsOfTyconRefToNameEnv bulkAddMode ownDefinition (g: TcGlobals)
12381255

12391256
{ nenv with eUnqualifiedItems = tab }
12401257

1241-
let nenv = AddStaticPartsOfTyconRefToNameEnv bulkAddMode ownDefinition g amap m nenv tcref
1258+
let nenv = AddStaticPartsOfTyconRefToNameEnv bulkAddMode ownDefinition g amap m nenv None tcref
12421259
let nenv =
12431260
if CanAutoOpenTyconRef g m tcref then
12441261
let ty = generalizedTyconRef tcref
@@ -1427,17 +1444,26 @@ let FreshenTyconWithEnclosingTypeInst (ncenv: NameResolver) m (tinstEnclosing: T
14271444

14281445
/// Convert a reference to a union case into a UnionCaseInfo that includes
14291446
/// a fresh set of inference type variables for the type parameters of the union type.
1430-
let FreshenUnionCaseRef (ncenv: NameResolver) m (ucref: UnionCaseRef) =
1447+
let FreshenUnionCaseRef (ncenv: NameResolver) m (ucref: UnionCaseRef) =
14311448
let tinst = ncenv.InstantiationGenerator m (ucref.TyconRef.Typars m)
14321449
UnionCaseInfo(tinst, ucref)
14331450

1434-
/// This must be called after fetching unqualified items that may need to be freshened
1435-
let FreshenUnqualifiedItem (ncenv: NameResolver) m res =
1451+
/// Generate a new reference to a record field with a fresh type instantiation
1452+
let FreshenRecdFieldRef (ncenv: NameResolver) m (rfref: RecdFieldRef) =
1453+
RecdFieldInfo(ncenv.InstantiationGenerator m (rfref.Tycon.Typars m), rfref)
1454+
1455+
/// This must be called after fetching unqualified items that may need to be freshened
1456+
/// or have type instantiations
1457+
let ResolveUnqualifiedItem (ncenv: NameResolver) nenv m res =
14361458
match res with
1437-
| Item.UnionCase(UnionCaseInfo(_, ucref), _) -> Item.UnionCase(FreshenUnionCaseRef ncenv m ucref, false)
1459+
| Item.UnionCase(UnionCaseInfo(_, ucref), _) ->
1460+
match nenv.eUnqualifiedRecordOrUnionTypeInsts.TryFind ucref.TyconRef with
1461+
| Some tinst ->
1462+
Item.UnionCase(UnionCaseInfo(tinst, ucref), false)
1463+
| _ ->
1464+
Item.UnionCase(FreshenUnionCaseRef ncenv m ucref, false)
14381465
| _ -> res
14391466

1440-
14411467
//-------------------------------------------------------------------------
14421468
// Resolve module paths, value, field etc. lookups. Doing this involves
14431469
// searching through many possibilities and disambiguating. Hence first
@@ -2777,7 +2803,7 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
27772803
None
27782804

27792805
| true, res ->
2780-
let fresh = FreshenUnqualifiedItem ncenv m res
2806+
let fresh = ResolveUnqualifiedItem ncenv nenv m res
27812807
match fresh with
27822808
| Item.Value value ->
27832809
let isNameOfOperator = valRefEq ncenv.g ncenv.g.nameof_vref value
@@ -2896,7 +2922,7 @@ let rec ResolveExprLongIdentPrim sink (ncenv: NameResolver) first fullyQualified
28962922
match nenv.eUnqualifiedItems.TryGetValue id.idText with
28972923
| true, Item.UnqualifiedType _
28982924
| false, _ -> NoResultsOrUsefulErrors
2899-
| true, res -> OneSuccess (ResolutionInfo.Empty, FreshenUnqualifiedItem ncenv m res, rest)
2925+
| true, res -> OneSuccess (ResolutionInfo.Empty, ResolveUnqualifiedItem ncenv nenv m res, rest)
29002926

29012927
moduleSearch ad () +++ tyconSearch ad +++ envSearch
29022928

@@ -3041,7 +3067,7 @@ let rec ResolvePatternLongIdentPrim sink (ncenv: NameResolver) fullyQualified wa
30413067
// For the special case of
30423068
// let C = x
30433069
match nenv.ePatItems.TryGetValue id.idText with
3044-
| true, res when not newDef -> FreshenUnqualifiedItem ncenv m res
3070+
| true, res when not newDef -> ResolveUnqualifiedItem ncenv nenv m res
30453071
| _ ->
30463072
// Single identifiers in patterns - variable bindings
30473073
if not newDef &&
@@ -3347,7 +3373,7 @@ let rec ResolveFieldInModuleOrNamespace (ncenv: NameResolver) nenv ad (resInfo:
33473373
match TryFindTypeWithRecdField modref id with
33483374
| Some tycon when IsEntityAccessible ncenv.amap m ad (modref.NestedTyconRef tycon) ->
33493375
let showDeprecated = HasFSharpAttribute ncenv.g ncenv.g.attrib_RequireQualifiedAccessAttribute tycon.Attribs
3350-
success [resInfo, FieldResolution(modref.RecdFieldRefInNestedTycon tycon id, showDeprecated), rest]
3376+
success [resInfo, FieldResolution(FreshenRecdFieldRef ncenv m (modref.RecdFieldRefInNestedTycon tycon id), showDeprecated), rest]
33513377
| _ -> raze (UndefinedName(depth, FSComp.SR.undefinedNameRecordLabelOrNamespace, id, NoSuggestions))
33523378

33533379
// search for type-qualified names, e.g. { Microsoft.FSharp.Core.Ref.contents = 1 }
@@ -3359,7 +3385,7 @@ let rec ResolveFieldInModuleOrNamespace (ncenv: NameResolver) nenv ad (resInfo:
33593385
let tcrefs = tcrefs |> List.map (fun tcref -> (ResolutionInfo.Empty, tcref))
33603386
let tyconSearch = ResolveLongIdentInTyconRefs ResultCollectionSettings.AllResults ncenv nenv LookupKind.RecdField (depth+1) m ad id2 rest2 typeNameResInfo id.idRange tcrefs
33613387
// choose only fields
3362-
let tyconSearch = tyconSearch |?> List.choose (function (resInfo, Item.RecdField(RecdFieldInfo(_, rfref)), rest) -> Some(resInfo, FieldResolution(rfref, false), rest) | _ -> None)
3388+
let tyconSearch = tyconSearch |?> List.choose (function (resInfo, Item.RecdField(RecdFieldInfo(_, rfref)), rest) -> Some(resInfo, FieldResolution(FreshenRecdFieldRef ncenv m rfref, false), rest) | _ -> None)
33633389
tyconSearch
33643390
| _ ->
33653391
NoResultsOrUsefulErrors
@@ -3453,12 +3479,17 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi
34533479
// Eliminate duplicates arising from multiple 'open'
34543480
frefs
34553481
|> ListSet.setify (fun fref1 fref2 -> tyconRefEq g fref1.TyconRef fref2.TyconRef)
3456-
|> List.map (fun x -> ResolutionInfo.Empty, FieldResolution(x, false))
3482+
|> List.map (fun x ->
3483+
let rfinfo =
3484+
match nenv.eUnqualifiedRecordOrUnionTypeInsts.TryFind x.TyconRef with
3485+
| Some tinst -> RecdFieldInfo(tinst, x)
3486+
| _ -> FreshenRecdFieldRef ncenv m x
3487+
ResolutionInfo.Empty, FieldResolution(rfinfo, false))
34573488

34583489
match tryTcrefOfAppTy g ty with
34593490
| ValueSome tcref ->
34603491
match ncenv.InfoReader.TryFindRecdOrClassFieldInfoOfType(id.idText, m, ty) with
3461-
| ValueSome (RecdFieldInfo(_, rfref)) -> [ResolutionInfo.Empty, FieldResolution(rfref, false)]
3492+
| ValueSome (RecdFieldInfo(_, rfref)) -> [ResolutionInfo.Empty, FieldResolution(FreshenRecdFieldRef ncenv m rfref, false)]
34623493
| _ ->
34633494
if tcref.IsRecordTycon then
34643495
// record label doesn't belong to record type -> suggest other labels of same record
@@ -3484,7 +3515,7 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi
34843515
let tcrefs = tcrefs |> List.map (fun tcref -> (ResolutionInfo.Empty, tcref))
34853516
let tyconSearch = ResolveLongIdentInTyconRefs ResultCollectionSettings.AllResults ncenv nenv LookupKind.RecdField 1 m ad id2 rest2 typeNameResInfo tn.idRange tcrefs
34863517
// choose only fields
3487-
let tyconSearch = tyconSearch |?> List.choose (function (resInfo, Item.RecdField(RecdFieldInfo(_, rfref)), rest) -> Some(resInfo, FieldResolution(rfref, false), rest) | _ -> None)
3518+
let tyconSearch = tyconSearch |?> List.choose (function (resInfo, Item.RecdField(RecdFieldInfo(_, rfref)), rest) -> Some(resInfo, FieldResolution(FreshenRecdFieldRef ncenv m rfref, false), rest) | _ -> None)
34883519
tyconSearch
34893520
| _ -> NoResultsOrUsefulErrors
34903521

@@ -3515,11 +3546,6 @@ let ResolveField sink ncenv nenv ad ty (mp, id) allFields =
35153546
ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, checker)
35163547
rfref)
35173548

3518-
/// Generate a new reference to a record field with a fresh type instantiation
3519-
let FreshenRecdFieldRef (ncenv: NameResolver) m (rfref: RecdFieldRef) =
3520-
Item.RecdField(RecdFieldInfo(ncenv.InstantiationGenerator m (rfref.Tycon.Typars m), rfref))
3521-
3522-
35233549
/// Resolve F#/IL "." syntax in expressions (2).
35243550
///
35253551
/// We have an expr. on the left, and we do an access, e.g.
@@ -3544,7 +3570,7 @@ let private ResolveExprDotLongIdent (ncenv: NameResolver) m ad nenv ty (id: Iden
35443570
| true, rfref :: _ ->
35453571
// NOTE (instantiationGenerator cleanup): we need to freshen here because we don't know the type.
35463572
// But perhaps the caller should freshen??
3547-
let item = FreshenRecdFieldRef ncenv m rfref
3573+
let item = Item.RecdField(FreshenRecdFieldRef ncenv m rfref)
35483574
OneSuccess (ResolutionInfo.Empty, item, rest)
35493575
| _ -> NoResultsOrUsefulErrors
35503576

src/fsharp/NameResolution.fsi

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ val (|ItemWithInst|) : ItemWithInst -> Item * TyparInst
137137
val ItemWithNoInst : Item -> ItemWithInst
138138

139139
/// Represents a record field resolution and the information if the usage is deprecated.
140-
type FieldResolution = FieldResolution of RecdFieldRef * bool
140+
type FieldResolution = FieldResolution of RecdFieldInfo * bool
141141

142142
/// Information about an extension member held in the name resolution environment
143143
type ExtensionMember =
@@ -181,6 +181,10 @@ type NameResolutionEnv =
181181
/// Bools indicate if from a record, where no warning is given on indeterminate lookup
182182
eFieldLabels: NameMultiMap<RecdFieldRef>
183183

184+
/// Record or unions that may have type instantiations associated with them
185+
/// when record labels or union cases are used in an unqualified context.
186+
eUnqualifiedRecordOrUnionTypeInsts: TyconRefMap<TypeInst>
187+
184188
/// Tycons indexed by the various names that may be used to access them, e.g.
185189
/// "List" --> multiple TyconRef's for the various tycons accessible by this name.
186190
/// "List`1" --> TyconRef
@@ -510,7 +514,7 @@ exception internal IndeterminateType of range
510514
exception internal UpperCaseIdentifierInPattern of range
511515

512516
/// Generate a new reference to a record field with a fresh type instantiation
513-
val FreshenRecdFieldRef :NameResolver -> Range.range -> RecdFieldRef -> Item
517+
val FreshenRecdFieldRef :NameResolver -> Range.range -> RecdFieldRef -> RecdFieldInfo
514518

515519
/// Indicates the kind of lookup being performed. Note, this type should be made private to nameres.fs.
516520
[<RequireQualifiedAccess>]

0 commit comments

Comments
 (0)