Skip to content

Commit 5f52cc2

Browse files
committed
Unify anonymous record fields
1 parent 9fadb2e commit 5f52cc2

35 files changed

+869
-663
lines changed

src/Compiler/Checking/CheckRecordSyntaxHelpers.fs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ let TransformAstForNestedUpdates (cenv: TcFileState) (env: TcEnv) overallTy (lid
100100
)
101101
| _ -> None
102102

103-
let rec synExprRecd copyInfo (outerFieldId: Ident) innerFields exprBeingAssigned =
103+
let rec synExprRecd copyInfo (outerFieldId: Ident) innerFields (exprBeingAssigned: SynExpr) =
104104
match innerFields with
105105
| [] -> failwith "unreachable"
106106
| (fieldId: Ident, item) :: rest ->
@@ -115,11 +115,13 @@ let TransformAstForNestedUpdates (cenv: TcFileState) (env: TcEnv) overallTy (lid
115115
synExprRecd copyInfo fieldId rest exprBeingAssigned
116116

117117
match item with
118-
| Item.AnonRecdField(
119-
anonInfo = {
120-
AnonRecdTypeInfo.TupInfo = TupInfo.Const isStruct
121-
}) ->
122-
let fields = [ LongIdentWithDots([ fieldId ], []), None, nestedField ]
118+
| Item.AnonRecdField(anonInfo = { TupInfo = TupInfo.Const isStruct }) ->
119+
let fieldName = (SynLongIdent([ fieldId ], [], [ None ]), true)
120+
let fieldRange = unionRanges fieldId.idRange nestedField.Range
121+
122+
let fields =
123+
[ SynExprRecordField(fieldName, None, Some nestedField, fieldRange, None) ]
124+
123125
SynExpr.AnonRecd(isStruct, copyInfo outerFieldId, fields, outerFieldId.idRange, { OpeningBraceRange = range0 })
124126
| _ ->
125127
let fields =

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7855,42 +7855,69 @@ and CheckAnonRecdExprDuplicateFields (elems: Ident array) =
78557855
errorR(Error (FSComp.SR.tcAnonRecdDuplicateFieldId(uc1.idText), uc1.idRange))))
78567856

78577857
// Check '{| .... |}'
7858-
and TcAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, optOrigSynExpr, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) =
7858+
and TcAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, optOrigSynExpr, recordFields: SynExprRecordField list, mWholeExpr) =
78597859
match optOrigSynExpr with
78607860
| None ->
7861-
TcNewAnonRecdExpr cenv overallTy env tpenv (isStruct, unsortedFieldIdsAndSynExprsGiven, mWholeExpr)
7861+
TcNewAnonRecdExpr cenv overallTy env tpenv (isStruct, recordFields, mWholeExpr)
78627862

78637863
| Some orig ->
78647864
// Ideally we should also check for duplicate field IDs in the TcCopyAndUpdateAnonRecdExpr case, but currently the logic is too complex to guarantee a proper error reporting
78657865
// So here we error instead errorR to avoid cascading internal errors
7866-
unsortedFieldIdsAndSynExprsGiven
7867-
|> List.countBy (fun (fId, _, _) -> textOfLid fId.LongIdent)
7866+
recordFields
7867+
|> List.countBy (fun (SynExprRecordField(fieldName = (fId, _))) -> textOfLid fId.LongIdent)
78687868
|> List.iter (fun (label, count) ->
78697869
if count > 1 then error (Error (FSComp.SR.tcAnonRecdDuplicateFieldId(label), mWholeExpr)))
78707870

7871-
TcCopyAndUpdateAnonRecdExpr cenv overallTy env tpenv (isStruct, orig, unsortedFieldIdsAndSynExprsGiven, mWholeExpr)
7871+
TcCopyAndUpdateAnonRecdExpr cenv overallTy env tpenv (isStruct, orig, recordFields, mWholeExpr)
78727872

7873-
and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) =
7873+
and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, recordFields: SynExprRecordField list, mWholeExpr) =
78747874

78757875
let g = cenv.g
7876-
let unsortedFieldSynExprsGiven = unsortedFieldIdsAndSynExprsGiven |> List.map (fun (_, _, fieldExpr) -> fieldExpr)
7877-
let unsortedFieldIds = unsortedFieldIdsAndSynExprsGiven |> List.map (fun (synLongIdent, _, _) -> synLongIdent.LongIdent[0]) |> List.toArray
7878-
let anonInfo, sortedFieldTys = UnifyAnonRecdTypeAndInferCharacteristics env.eContextInfo cenv env.DisplayEnv mWholeExpr overallTy isStruct unsortedFieldIds
7876+
7877+
// For new anonymous records, each field label must be a single identifier.
7878+
// If a long identifier is given, report an error and skip that entry for typechecking purposes.
7879+
// Also normalize missing expressions to an error expression to keep traversal stable.
7880+
let validFields : (Ident * SynExpr * SynLongIdent) list =
7881+
recordFields
7882+
|> List.choose (fun (SynExprRecordField(fieldName = (synLongIdent, _); expr = expr)) ->
7883+
match synLongIdent.LongIdent with
7884+
| [ id ] ->
7885+
let exprGiven =
7886+
match expr with
7887+
| Some expr -> expr
7888+
| None -> arbExpr ("anonField", synLongIdent.Range)
7889+
Some(id, exprGiven, synLongIdent)
7890+
| [] ->
7891+
// Should not occur for anonymous record expressions; silently skip with a diagnostic.
7892+
errorR (Error(FSComp.SR.parsInvalidAnonRecdType(), synLongIdent.Range))
7893+
None
7894+
| _ ->
7895+
// Nested labels are only valid in copy-and-update; not allowed for new anonymous records
7896+
errorR (Error(FSComp.SR.parsInvalidAnonRecdType(), synLongIdent.Range))
7897+
None)
7898+
7899+
let unsortedFieldSynExprsGiven = validFields |> List.map (fun (_, e, _) -> e)
7900+
7901+
let unsortedFieldIds = validFields |> List.map (fun (id, _, _) -> id) |> List.toArray
7902+
7903+
let anonInfo, sortedFieldTys =
7904+
UnifyAnonRecdTypeAndInferCharacteristics env.eContextInfo cenv env.DisplayEnv mWholeExpr overallTy isStruct unsortedFieldIds
78797905

78807906
if unsortedFieldIds.Length > 1 then
78817907
CheckAnonRecdExprDuplicateFields unsortedFieldIds
78827908

78837909
// Sort into canonical order
78847910
let sortedIndexedArgs =
7885-
unsortedFieldIdsAndSynExprsGiven
7911+
validFields
78867912
|> List.indexed
78877913
|> List.sortBy (fun (i,_) -> unsortedFieldIds[i].idText)
78887914

78897915
// Map from sorted indexes to unsorted indexes
78907916
let sigma = sortedIndexedArgs |> List.map fst |> List.toArray
7891-
let sortedFieldExprs = sortedIndexedArgs |> List.map snd
7917+
let sortedFieldTriples = sortedIndexedArgs |> List.map snd
78927918

7893-
sortedFieldExprs |> List.iteri (fun j (synLongIdent, _, _) ->
7919+
sortedFieldTriples
7920+
|> List.iteri (fun j (_, _, synLongIdent) ->
78947921
let m = rangeOfLid synLongIdent.LongIdent
78957922
let item = Item.AnonRecdField(anonInfo, sortedFieldTys, j, m)
78967923
CallNameResolutionSink cenv.tcSink (m, env.NameEnv, item, emptyTyparInst, ItemOccurrence.Use, env.eAccessRights))
@@ -7903,11 +7930,12 @@ and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, unsortedField
79037930

79047931
let flexes = unsortedFieldTys |> List.map (fun _ -> true)
79057932

7906-
let unsortedCheckedArgs, tpenv = TcExprsWithFlexes cenv env mWholeExpr tpenv flexes unsortedFieldTys unsortedFieldSynExprsGiven
7933+
let unsortedCheckedArgs, tpenv =
7934+
TcExprsWithFlexes cenv env mWholeExpr tpenv flexes unsortedFieldTys unsortedFieldSynExprsGiven
79077935

79087936
mkAnonRecd g mWholeExpr anonInfo unsortedFieldIds unsortedCheckedArgs unsortedFieldTys, tpenv
79097937

7910-
and TcCopyAndUpdateAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, (origExpr, blockSeparator), unsortedFieldIdsAndSynExprsGiven, mWholeExpr) =
7938+
and TcCopyAndUpdateAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, (origExpr, blockSeparator), recordFields: SynExprRecordField list, mWholeExpr) =
79117939
// The fairly complex case '{| origExpr with X = 1; Y = 2 |}'
79127940
// The origExpr may be either a record or anonymous record.
79137941
// The origExpr may be either a struct or not.
@@ -7926,14 +7954,17 @@ and TcCopyAndUpdateAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, (or
79267954
if not (isAppTy g origExprTy || isAnonRecdTy g origExprTy) then
79277955
error (Error (FSComp.SR.tcCopyAndUpdateNeedsRecordType(), mOrigExpr))
79287956

7929-
// Expand expressions with respect to potential nesting
7957+
// Expand expressions with respect to potentially nesting
79307958
let unsortedFieldIdsAndSynExprsGiven =
7931-
unsortedFieldIdsAndSynExprsGiven
7932-
|> List.map (fun (synLongIdent, _, exprBeingAssigned) ->
7959+
recordFields
7960+
|> List.map (fun (SynExprRecordField(fieldName = (synLongIdent, _); expr = e)) ->
79337961
match synLongIdent.LongIdent with
79347962
| [] -> error(Error(FSComp.SR.nrUnexpectedEmptyLongId(), mWholeExpr))
7935-
| [ id ] -> ([], id), Some exprBeingAssigned
7936-
| lid -> TransformAstForNestedUpdates cenv env origExprTy lid exprBeingAssigned (origExpr, blockSeparator))
7963+
| [ id ] -> ([], id), e
7964+
| lid ->
7965+
match e with
7966+
| Some exprBeingAssigned -> TransformAstForNestedUpdates cenv env origExprTy lid exprBeingAssigned (origExpr, blockSeparator)
7967+
| None -> List.frontAndBack lid, None)
79377968
|> GroupUpdatesToNestedFields
79387969

79397970
let unsortedFieldSynExprsGiven = unsortedFieldIdsAndSynExprsGiven |> List.choose snd

src/Compiler/Driver/GraphChecking/FileContentMapping.fs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,15 @@ let visitSynExpr (e: SynExpr) : FileContentEntry list =
378378
| SynExpr.AnonRecd(copyInfo = copyInfo; recordFields = recordFields) ->
379379
let continuations =
380380
match copyInfo with
381-
| None -> List.map (fun (_, _, e) -> visit e) recordFields
382-
| Some(cp, _) -> visit cp :: List.map (fun (_, _, e) -> visit e) recordFields
381+
| None ->
382+
recordFields
383+
|> List.choose (fun (SynExprRecordField(expr = e)) -> e)
384+
|> List.map visit
385+
| Some(cp, _) ->
386+
visit cp
387+
:: (recordFields
388+
|> List.choose (fun (SynExprRecordField(expr = e)) -> e)
389+
|> List.map visit)
383390

384391
Continuation.concatenate continuations continuation
385392
| SynExpr.ArrayOrList(exprs = exprs) ->

src/Compiler/Service/FSharpParseFileResults.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,
641641
| Some(e, _) -> yield! walkExpr true e
642642
| None -> ()
643643

644-
yield! walkExprs (fs |> List.map (fun (_, _, e) -> e))
644+
yield! walkExprs (fs |> List.choose (fun (SynExprRecordField(expr = e)) -> e))
645645

646646
| SynExpr.ObjExpr(argOptions = args; bindings = bs; members = ms; extraImpls = is) ->
647647
let bs = unionBindingAndMembers bs ms

0 commit comments

Comments
 (0)