Skip to content

Commit 9e1d654

Browse files
Error on recursive type spreads
1 parent 46998d6 commit 9e1d654

16 files changed

+271
-13
lines changed

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,6 +2617,8 @@ module EstablishTypeDefinitionCores =
26172617
let m = tycon.Range
26182618
let env = AddDeclaredTypars CheckForDuplicateTypars (tycon.Typars m) env
26192619
let env = MakeInnerEnvForTyconRef env thisTyconRef false
2620+
let ad = env.AccessRights
2621+
let spreadSrcTys = ResizeArray ()
26202622
[ match synTyconRepr with
26212623
| SynTypeDefnSimpleRepr.None _ -> ()
26222624
| SynTypeDefnSimpleRepr.Union (_, unionCases, _) ->
@@ -2660,13 +2662,62 @@ module EstablishTypeDefinitionCores =
26602662
errorR(Error(FSComp.SR.tcStructsMustDeclareTypesOfImplicitCtorArgsExplicitly(), m))
26612663
yield (ty, m)
26622664

2663-
| SynTypeDefnSimpleRepr.Record (_, SynFields fields, _) ->
2664-
for SynField(fieldType = ty; range = m) in fields do
2665-
let tyR, _ = TcTypeAndRecover cenv NoNewTypars NoCheckCxs ItemOccurrence.UseInType WarnOnIWSAM.Yes env tpenv ty
2666-
yield (tyR, m)
2665+
| SynTypeDefnSimpleRepr.Record (_, fieldsAndSpreads, _) ->
2666+
let (|LeftwardExplicit|NoLeftwardExplicit|) hasLeftwardExplicit = if hasLeftwardExplicit then LeftwardExplicit else NoLeftwardExplicit
2667+
let LeftwardExplicit = true
2668+
let NoLeftwardExplicit = false
2669+
2670+
// We must apply the spread shadowing logic here to get
2671+
// the correct set of field types.
2672+
let rec collectTys tys fieldsAndSpreads =
2673+
match fieldsAndSpreads with
2674+
| [] ->
2675+
tys
2676+
|> Map.toList
2677+
|> List.collect (fun (_, (_, dupes)) -> dupes)
2678+
2679+
| SynFieldOrSpread.Field (SynField (idOpt = None)) :: fieldsAndSpreads ->
2680+
collectTys tys fieldsAndSpreads
2681+
2682+
| SynFieldOrSpread.Field (SynField (idOpt = Some fieldId; fieldType = ty; range = m)) :: fieldsAndSpreads ->
2683+
let tyR, _ = TcTypeAndRecover cenv NoNewTypars NoCheckCxs ItemOccurrence.UseInType WarnOnIWSAM.Yes env tpenv ty
2684+
let tys =
2685+
tys |> Map.change fieldId.idText (function
2686+
| None -> Some (LeftwardExplicit, [tyR, m])
2687+
| Some (LeftwardExplicit, dupes) -> Some (LeftwardExplicit, (tyR, m) :: dupes)
2688+
| Some (NoLeftwardExplicit, _dupes) -> Some (LeftwardExplicit, [tyR, m]))
2689+
2690+
collectTys tys fieldsAndSpreads
2691+
2692+
| SynFieldOrSpread.Spread (SynTypeSpread (ty = ty; range = m)) :: fieldsAndSpreads ->
2693+
let spreadSrcTy, _ = TcTypeAndRecover cenv NoNewTypars NoCheckCxs ItemOccurrence.UseInType WarnOnIWSAM.Yes env tpenv ty
2694+
2695+
let fieldsFromSpread =
2696+
if isRecdTy g spreadSrcTy then
2697+
spreadSrcTys.Add spreadSrcTy
2698+
ResolveRecordOrClassFieldsOfType cenv.nameResolver m ad spreadSrcTy false
2699+
|> List.choose (function
2700+
| Item.RecdField field -> Some (field.RecdField.Id.idText, (FreshenRecdFieldRef cenv.nameResolver m field.RecdFieldRef).FieldType, m)
2701+
| _ -> None)
2702+
else
2703+
match tryDestAnonRecdTy g spreadSrcTy with
2704+
| ValueSome (anonInfo, tys) -> tys |> List.mapi (fun i ty -> anonInfo.SortedNames[i], ty, m)
2705+
| ValueNone -> []
2706+
2707+
let tys =
2708+
(tys, fieldsFromSpread)
2709+
||> List.fold (fun tys (fieldId, ty, m) ->
2710+
tys |> Map.change fieldId (function
2711+
| None -> Some (NoLeftwardExplicit, [ty, m])
2712+
| Some (LeftwardExplicit, _dupes) -> Some (LeftwardExplicit, [ty, m])
2713+
| Some (NoLeftwardExplicit, _dupes) -> Some (NoLeftwardExplicit, [ty, m])))
2714+
2715+
collectTys tys fieldsAndSpreads
2716+
2717+
yield! collectTys Map.empty fieldsAndSpreads
26672718

26682719
| _ ->
2669-
() ]
2720+
() ], spreadSrcTys
26702721

26712722
let ComputeModuleOrNamespaceKind g isModule typeNames attribs nm =
26722723
if not isModule then (Namespace true)
@@ -4317,14 +4368,43 @@ module EstablishTypeDefinitionCores =
43174368
// be satisfied, so we have to do this prior to checking any constraints.
43184369
//
43194370
// First find all the field types in all the structural types
4320-
let tyconsWithStructuralTypes =
4321-
(envMutRecPrelim, withEnvs)
4322-
||> MutRecShapes.mapTyconsWithEnv (fun envForDecls (origInfo, tyconOpt) ->
4323-
match origInfo, tyconOpt with
4324-
| (typeDefCore, _, _), Some tycon -> Some (tycon, GetStructuralElementsOfTyconDefn cenv envForDecls tpenv typeDefCore tycon)
4325-
| _ -> None)
4326-
|> MutRecShapes.collectTycons
4327-
|> List.choose id
4371+
let tyconsWithStructuralTypes =
4372+
let all =
4373+
(envMutRecPrelim, withEnvs)
4374+
||> MutRecShapes.mapTyconsWithEnv (fun envForDecls (origInfo, tyconOpt) ->
4375+
match origInfo, tyconOpt with
4376+
| (typeDefCore, _, _), Some tycon -> Some (tycon, GetStructuralElementsOfTyconDefn cenv envForDecls tpenv typeDefCore tycon)
4377+
| _ -> None)
4378+
|> MutRecShapes.collectTycons
4379+
|> List.choose id
4380+
4381+
// Check for cyclic spreads.
4382+
do
4383+
if cenv.g.langVersion.SupportsFeature LanguageFeature.RecordSpreads then
4384+
let (|PotentiallyRecursiveTycon|_|) ty =
4385+
tryTcrefOfAppTy cenv.g ty
4386+
|> ValueOption.bind _.TryDeref
4387+
4388+
let edges =
4389+
[
4390+
for dst, (_, spreadSrcs) in all do
4391+
for src in spreadSrcs do
4392+
match src with
4393+
| PotentiallyRecursiveTycon src -> dst, src
4394+
| _ -> ()
4395+
]
4396+
4397+
let tycons =
4398+
[
4399+
for dst, src in edges do
4400+
yield dst
4401+
yield src
4402+
]
4403+
4404+
let graph = Graph<Tycon, Stamp> (_.Stamp, tycons, edges)
4405+
graph.IterateCycles (fun path -> errorR (Error (FSComp.SR.tcTypeDefinitionIsCyclicThroughSpreads (), (List.head path).Range)))
4406+
4407+
[for tycon, (tys, _) in all -> tycon, tys]
43284408

43294409
let scSet = TyconConstraintInference.InferSetOfTyconsSupportingComparable cenv envMutRecPrelim.DisplayEnv tyconsWithStructuralTypes
43304410
let seSet = TyconConstraintInference.InferSetOfTyconsSupportingEquatable cenv envMutRecPrelim.DisplayEnv tyconsWithStructuralTypes

src/Compiler/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,4 +1821,5 @@ featureReturnFromFinal,"Support for ReturnFromFinal/YieldFromFinal in computatio
18211821
3883,tcRecordExprSpreadFieldShadowsExplicitField,"Spread field '%s' shadows an explicitly declared field with the same name."
18221822
3884,parsMissingSpreadSrcExpr,"Missing spread source expression after '...'."
18231823
3885,parsMissingSpreadSrcTy,"Missing spread source type after '...'."
1824+
3886,tcTypeDefinitionIsCyclicThroughSpreads,"This type definition involves a cyclic reference through a spread."
18241825
featureRecordSpreads,"record type and expression spreads"

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.ja.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.ko.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.pl.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)