Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/Compiler/Checking/CheckRecordSyntaxHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ let TransformAstForNestedUpdates (cenv: TcFileState) (env: TcEnv) overallTy (lid
)
| _ -> None

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

match item with
| Item.AnonRecdField(
anonInfo = {
AnonRecdTypeInfo.TupInfo = TupInfo.Const isStruct
}) ->
let fields = [ LongIdentWithDots([ fieldId ], []), None, nestedField ]
| Item.AnonRecdField(anonInfo = { TupInfo = TupInfo.Const isStruct }) ->
let fieldName = (SynLongIdent([ fieldId ], [], [ None ]), true)
let fieldRange = unionRanges fieldId.idRange nestedField.Range

let fields =
[ SynExprRecordField(fieldName, None, Some nestedField, fieldRange, None) ]

SynExpr.AnonRecd(isStruct, copyInfo outerFieldId, fields, outerFieldId.idRange, { OpeningBraceRange = range0 })
| _ ->
let fields =
Expand Down
57 changes: 44 additions & 13 deletions src/Compiler/Checking/Expressions/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7855,7 +7855,7 @@ and CheckAnonRecdExprDuplicateFields (elems: Ident array) =
errorR(Error (FSComp.SR.tcAnonRecdDuplicateFieldId(uc1.idText), uc1.idRange))))

// Check '{| .... |}'
and TcAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, optOrigSynExpr, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) =
and TcAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, optOrigSynExpr, unsortedFieldIdsAndSynExprsGiven: SynExprRecordField list, mWholeExpr) =
match optOrigSynExpr with
| None ->
TcNewAnonRecdExpr cenv overallTy env tpenv (isStruct, unsortedFieldIdsAndSynExprsGiven, mWholeExpr)
Expand All @@ -7864,18 +7864,44 @@ and TcAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, optOrigSynExpr,
// 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
// So here we error instead errorR to avoid cascading internal errors
unsortedFieldIdsAndSynExprsGiven
|> List.countBy (fun (fId, _, _) -> textOfLid fId.LongIdent)
|> List.countBy (fun (SynExprRecordField(fieldName = (fId, _))) -> textOfLid fId.LongIdent)
|> List.iter (fun (label, count) ->
if count > 1 then error (Error (FSComp.SR.tcAnonRecdDuplicateFieldId(label), mWholeExpr)))

TcCopyAndUpdateAnonRecdExpr cenv overallTy env tpenv (isStruct, orig, unsortedFieldIdsAndSynExprsGiven, mWholeExpr)

and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) =
and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, unsortedFieldIdsAndSynExprsGiven: SynExprRecordField list, mWholeExpr) =

let g = cenv.g
let unsortedFieldSynExprsGiven = unsortedFieldIdsAndSynExprsGiven |> List.map (fun (_, _, fieldExpr) -> fieldExpr)
let unsortedFieldIds = unsortedFieldIdsAndSynExprsGiven |> List.map (fun (synLongIdent, _, _) -> synLongIdent.LongIdent[0]) |> List.toArray
let anonInfo, sortedFieldTys = UnifyAnonRecdTypeAndInferCharacteristics env.eContextInfo cenv env.DisplayEnv mWholeExpr overallTy isStruct unsortedFieldIds

// For new anonymous records, each field label must be a single identifier.
// If a long identifier is given, report an error and skip that entry for typechecking purposes.
// Also normalize missing expressions to an error expression to keep traversal stable.
let unsortedFieldIdsAndSynExprsGiven : (Ident * SynExpr * SynLongIdent) list =
unsortedFieldIdsAndSynExprsGiven
|> List.choose (fun (SynExprRecordField(fieldName = (synLongIdent, _); expr = expr)) ->
match synLongIdent.LongIdent with
| [ id ] ->
let exprGiven =
match expr with
| Some expr -> expr
| None -> arbExpr ("anonField", synLongIdent.Range)
Some(id, exprGiven, synLongIdent)
| [] ->
// Should not occur for anonymous record expressions; silently skip with a diagnostic.
errorR (Error(FSComp.SR.parsInvalidAnonRecdType(), synLongIdent.Range))
None
| _ ->
// Nested labels are only valid in copy-and-update; not allowed for new anonymous records
errorR (Error(FSComp.SR.parsInvalidAnonRecdType(), synLongIdent.Range))
None)

let unsortedFieldSynExprsGiven = unsortedFieldIdsAndSynExprsGiven |> List.map (fun (_, e, _) -> e)

let unsortedFieldIds = unsortedFieldIdsAndSynExprsGiven |> List.map (fun (id, _, _) -> id) |> List.toArray

let anonInfo, sortedFieldTys =
UnifyAnonRecdTypeAndInferCharacteristics env.eContextInfo cenv env.DisplayEnv mWholeExpr overallTy isStruct unsortedFieldIds

if unsortedFieldIds.Length > 1 then
CheckAnonRecdExprDuplicateFields unsortedFieldIds
Expand All @@ -7890,7 +7916,8 @@ and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, unsortedField
let sigma = sortedIndexedArgs |> List.map fst |> List.toArray
let sortedFieldExprs = sortedIndexedArgs |> List.map snd

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

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

let unsortedCheckedArgs, tpenv = TcExprsWithFlexes cenv env mWholeExpr tpenv flexes unsortedFieldTys unsortedFieldSynExprsGiven
let unsortedCheckedArgs, tpenv =
TcExprsWithFlexes cenv env mWholeExpr tpenv flexes unsortedFieldTys unsortedFieldSynExprsGiven

mkAnonRecd g mWholeExpr anonInfo unsortedFieldIds unsortedCheckedArgs unsortedFieldTys, tpenv

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

// Expand expressions with respect to potential nesting
// Expand expressions with respect to potentially nesting
let unsortedFieldIdsAndSynExprsGiven =
unsortedFieldIdsAndSynExprsGiven
|> List.map (fun (synLongIdent, _, exprBeingAssigned) ->
|> List.map (fun (SynExprRecordField(fieldName = (synLongIdent, _); expr = e)) ->
match synLongIdent.LongIdent with
| [] -> error(Error(FSComp.SR.nrUnexpectedEmptyLongId(), mWholeExpr))
| [ id ] -> ([], id), Some exprBeingAssigned
| lid -> TransformAstForNestedUpdates cenv env origExprTy lid exprBeingAssigned (origExpr, blockSeparator))
| [ id ] -> ([], id), e
| lid ->
match e with
| Some exprBeingAssigned -> TransformAstForNestedUpdates cenv env origExprTy lid exprBeingAssigned (origExpr, blockSeparator)
| None -> List.frontAndBack lid, None)
|> GroupUpdatesToNestedFields

let unsortedFieldSynExprsGiven = unsortedFieldIdsAndSynExprsGiven |> List.choose snd
Expand Down
34 changes: 16 additions & 18 deletions src/Compiler/Driver/GraphChecking/FileContentMapping.fs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,19 @@ let (|NameofExpr|_|) (e: SynExpr) : NameofResult voption =
| _ -> ValueNone
| _ -> ValueNone

let visitRecordLikeExpr baseInfo copyInfo (recordFields: SynExprRecordField list) : FileContentEntry list =
let fieldNodes =
[
for SynExprRecordField(fieldName = (si, _); expr = expr) in recordFields do
yield! visitSynLongIdent si
yield! collectFromOption visitSynExpr expr
]

match baseInfo, copyInfo with
| Some(t, e, _, _, _), None -> visitSynType t @ visitSynExpr e @ fieldNodes
| None, Some(e, _) -> visitSynExpr e @ fieldNodes
| _ -> fieldNodes

let visitSynExpr (e: SynExpr) : FileContentEntry list =
let rec visit (e: SynExpr) (continuation: FileContentEntry list -> FileContentEntry list) : FileContentEntry list =
match e with
Expand All @@ -374,29 +387,14 @@ let visitSynExpr (e: SynExpr) : FileContentEntry list =
let continuations: ((FileContentEntry list -> FileContentEntry list) -> FileContentEntry list) list =
List.map visit exprs

Continuation.concatenate continuations continuation
| SynExpr.AnonRecd(copyInfo = copyInfo; recordFields = recordFields) ->
let continuations =
match copyInfo with
| None -> List.map (fun (_, _, e) -> visit e) recordFields
| Some(cp, _) -> visit cp :: List.map (fun (_, _, e) -> visit e) recordFields

Continuation.concatenate continuations continuation
| SynExpr.ArrayOrList(exprs = exprs) ->
let continuations = List.map visit exprs
Continuation.concatenate continuations continuation
| SynExpr.AnonRecd(copyInfo = copyInfo; recordFields = recordFields) ->
continuation (visitRecordLikeExpr None copyInfo recordFields)
| SynExpr.Record(baseInfo = baseInfo; copyInfo = copyInfo; recordFields = recordFields) ->
let fieldNodes =
[
for SynExprRecordField(fieldName = (si, _); expr = expr) in recordFields do
yield! visitSynLongIdent si
yield! collectFromOption visitSynExpr expr
]

match baseInfo, copyInfo with
| Some(t, e, _, _, _), None -> visit e (fun nodes -> [ yield! visitSynType t; yield! nodes; yield! fieldNodes ] |> continuation)
| None, Some(e, _) -> visit e (fun nodes -> nodes @ fieldNodes |> continuation)
| _ -> continuation fieldNodes
continuation (visitRecordLikeExpr baseInfo copyInfo recordFields)
| SynExpr.New(targetType = targetType; expr = expr) -> visit expr (fun nodes -> visitSynType targetType @ nodes |> continuation)
| SynExpr.ObjExpr(objType, argOptions, _, bindings, members, extraImpls, _, _) ->
[
Expand Down
24 changes: 11 additions & 13 deletions src/Compiler/Service/FSharpParseFileResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,15 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,
| _ -> ()
]

and walkRecordLike (copyExprOpt: (SynExpr * BlockSeparator) option) (fs: SynExprRecordField list) =
[
match copyExprOpt with
| Some(e, _) -> yield! walkExpr true e
| None -> ()

yield! walkExprs (fs |> List.choose (fun (SynExprRecordField(expr = e)) -> e))
]

// Determine the breakpoint locations for an expression. spImplicit indicates we always
// emit a breakpoint location for the expression unless it is a syntactic control flow construct
and walkExpr spImplicit expr =
Expand Down Expand Up @@ -629,19 +638,8 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,
| SynExpr.ArrayOrList(_, exprs, _)
| SynExpr.Tuple(_, exprs, _, _) -> yield! walkExprs exprs

| SynExpr.Record(_, copyExprOpt, fs, _) ->
match copyExprOpt with
| Some(e, _) -> yield! walkExpr true e
| None -> ()

yield! walkExprs (fs |> List.choose (fun (SynExprRecordField(expr = e)) -> e))

| SynExpr.AnonRecd(copyInfo = copyExprOpt; recordFields = fs) ->
match copyExprOpt with
| Some(e, _) -> yield! walkExpr true e
| None -> ()

yield! walkExprs (fs |> List.map (fun (_, _, e) -> e))
| SynExpr.Record(_, copyExprOpt, fs, _)
| SynExpr.AnonRecd(copyInfo = copyExprOpt; recordFields = fs) -> yield! walkRecordLike copyExprOpt fs

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