Skip to content

Commit 2f55c5a

Browse files
committed
Added support for erased unions
1 parent a377994 commit 2f55c5a

File tree

5 files changed

+77
-61
lines changed

5 files changed

+77
-61
lines changed

src/Fable.AST/Plugins.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type Verbosity =
1010
| Silent
1111

1212
type CompilerOptions =
13+
abstract EraseUnions: bool
1314
abstract TypedArrays: bool
1415
abstract ClampByteArrays: bool
1516
abstract Typescript: bool

src/Fable.Cli/Entry.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ type Runner =
138138
argValue "--extension" args |> Option.defaultValue (defaultFileExt typescript args)
139139

140140
let compilerOptions =
141-
CompilerOptionsHelper.Make(typescript = typescript,
141+
CompilerOptionsHelper.Make(eraseUnions = flagEnabled "--eraseUnions" args,
142+
typescript = typescript,
142143
typedArrays = typedArrays,
143144
fileExtension = fileExt,
144145
define = define,

src/Fable.Transforms/FSharp2Fable.Util.fs

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -675,33 +675,52 @@ module Patterns =
675675
| _ -> None
676676
else None
677677

678-
let (|OptionUnion|ListUnion|ErasedUnion|ErasedUnionCase|StringEnum|DiscriminatedUnion|)
679-
(NonAbbreviatedType typ: FSharpType, unionCase: FSharpUnionCase) =
678+
[<RequireQualifiedAccess>]
679+
type EraseKind =
680+
| AsValue
681+
| AsTuple
682+
| AsNamedTuple of CaseRules
683+
684+
let (|OptionUnion|ListUnion|ErasedUnion|DiscriminatedUnion|)
685+
(com: Compiler, NonAbbreviatedType typ: FSharpType, unionCase: FSharpUnionCase) =
686+
680687
let getCaseRule (att: FSharpAttribute) =
681688
match Seq.tryHead att.ConstructorArguments with
682689
| Some(_, (:? int as rule)) -> enum<CaseRules>(rule)
683690
| _ -> CaseRules.LowerFirst
684691

685-
unionCase.Attributes |> Seq.tryPick (fun att ->
686-
match att.AttributeType.TryFullName with
687-
| Some Atts.erase -> Some ErasedUnionCase
688-
| _ -> None)
689-
|> Option.defaultWith (fun () ->
690-
match tryDefinition typ with
691-
| None -> failwith "Union without definition"
692-
| Some(tdef, fullName) ->
693-
match defaultArg fullName tdef.CompiledName with
694-
| Types.valueOption
695-
| Types.option -> OptionUnion typ.GenericArguments.[0]
696-
| Types.list -> ListUnion typ.GenericArguments.[0]
697-
| _ ->
692+
let getEraseKind (tdef: FSharpEntity) caseRule =
693+
if tdef.UnionCases.Count = 1 && tdef.UnionCases.[0].UnionCaseFields.Count = 1
694+
then EraseKind.AsValue
695+
else EraseKind.AsNamedTuple(caseRule)
696+
697+
match tryDefinition typ with
698+
| None -> failwith "Union without definition"
699+
| Some(tdef, fullName) ->
700+
let fullName = defaultArg fullName tdef.CompiledName
701+
match fullName with
702+
| Types.valueOption
703+
| Types.option -> OptionUnion typ.GenericArguments.[0]
704+
| Types.list -> ListUnion typ.GenericArguments.[0]
705+
| _ ->
706+
unionCase.Attributes |> Seq.tryPick (fun att ->
707+
match att.AttributeType.TryFullName with
708+
| Some Atts.erase ->
709+
Some (ErasedUnion(EraseKind.AsTuple, tdef, typ.GenericArguments))
710+
| _ -> None)
711+
|> Option.orElseWith (fun () ->
698712
tdef.Attributes |> Seq.tryPick (fun att ->
699713
match att.AttributeType.TryFullName with
700-
| Some Atts.erase -> Some (ErasedUnion(tdef, typ.GenericArguments, getCaseRule att))
701-
| Some Atts.stringEnum -> Some (StringEnum(tdef, getCaseRule att))
702-
| _ -> None)
703-
|> Option.defaultValue (DiscriminatedUnion(tdef, typ.GenericArguments))
704-
)
714+
| Some Atts.erase
715+
| Some Atts.stringEnum ->
716+
let kind = getEraseKind tdef (getCaseRule att)
717+
Some (ErasedUnion(kind, tdef, typ.GenericArguments))
718+
| _ -> None))
719+
|> Option.defaultWith (fun () ->
720+
if com.Options.EraseUnions then
721+
let kind = getEraseKind tdef CaseRules.None
722+
ErasedUnion(kind, tdef, typ.GenericArguments)
723+
else DiscriminatedUnion(tdef, typ.GenericArguments))
705724

706725
let (|ContainsAtt|_|) (fullName: string) (ent: FSharpEntity) =
707726
tryFindAtt fullName ent.Attributes

src/Fable.Transforms/FSharp2Fable.fs

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,14 @@ let private transformBaseConsCall com ctx r (baseEnt: FSharpEntity) (baseCons: F
5050
| e -> e
5151

5252
let private transformNewUnion com ctx r fsType (unionCase: FSharpUnionCase) (argExprs: Fable.Expr list) =
53-
match fsType, unionCase with
54-
| ErasedUnionCase ->
55-
Fable.NewTuple argExprs |> makeValue r
56-
| ErasedUnion(tdef, _genArgs, rule) ->
57-
match argExprs with
58-
| [] -> transformStringEnum rule unionCase
59-
| [argExpr] -> argExpr
60-
| _ when tdef.UnionCases.Count > 1 ->
61-
"Erased unions with multiple cases must have one single field: " + (getFsTypeFullName fsType)
62-
|> addErrorAndReturnNull com ctx.InlinePath r
63-
| argExprs -> Fable.NewTuple argExprs |> makeValue r
64-
| StringEnum(tdef, rule) ->
65-
match argExprs with
66-
| [] -> transformStringEnum rule unionCase
67-
| _ -> sprintf "StringEnum types cannot have fields: %O" tdef.TryFullName
68-
|> addErrorAndReturnNull com ctx.InlinePath r
53+
match com, fsType, unionCase with
54+
| ErasedUnion(kind, tdef, _genArgs) ->
55+
match kind, argExprs with
56+
| EraseKind.AsNamedTuple caseRule, [] -> transformStringEnum caseRule unionCase
57+
| EraseKind.AsNamedTuple _, _ -> (makeStrConst unionCase.Name)::argExprs |> Fable.NewTuple |> makeValue r
58+
| EraseKind.AsValue, [arg] -> arg
59+
| EraseKind.AsValue, _ -> failwith "Shouldn't happen, error?"
60+
| EraseKind.AsTuple, _ -> Fable.NewTuple argExprs |> makeValue r
6961
| OptionUnion typ ->
7062
let typ = makeType ctx.GenericArgs typ
7163
let expr =
@@ -228,14 +220,16 @@ let private transformUnionCaseTest (com: IFableCompiler) (ctx: Context) r
228220
unionExpr fsType (unionCase: FSharpUnionCase) =
229221
trampoline {
230222
let! unionExpr = transformExpr com ctx unionExpr
231-
match fsType, unionCase with
232-
| ErasedUnionCase ->
233-
return "Cannot test erased union cases"
234-
|> addErrorAndReturnNull com ctx.InlinePath r
235-
| ErasedUnion(tdef, genArgs, rule) ->
236-
match unionCase.UnionCaseFields.Count with
237-
| 0 -> return makeEqOp r unionExpr (transformStringEnum rule unionCase) BinaryEqualStrict
238-
| 1 ->
223+
match com, fsType, unionCase with
224+
| ErasedUnion(kind, tdef, genArgs) ->
225+
match kind with
226+
| EraseKind.AsNamedTuple caseRule ->
227+
if unionCase.UnionCaseFields.Count = 0 then
228+
return makeEqOp r unionExpr (transformStringEnum caseRule unionCase) BinaryEqualStrict
229+
else
230+
let name = Fable.Get(unionExpr, Fable.TupleIndex(0), Fable.String, None)
231+
return makeEqOp r name (makeStrConst unionCase.Name) BinaryEqualStrict
232+
| EraseKind.AsValue ->
239233
let fi = unionCase.UnionCaseFields.[0]
240234
let typ =
241235
if fi.FieldType.IsGenericParameter then
@@ -247,17 +241,15 @@ let private transformUnionCaseTest (com: IFableCompiler) (ctx: Context) r
247241
else fi.FieldType
248242
let kind = makeType ctx.GenericArgs typ |> Fable.TypeTest
249243
return Fable.Test(unionExpr, kind, r)
250-
| _ ->
251-
return "Erased unions with multiple cases cannot have more than one field: " + (getFsTypeFullName fsType)
244+
| EraseKind.AsTuple ->
245+
return "Cannot test erased union cases"
252246
|> addErrorAndReturnNull com ctx.InlinePath r
253247
| OptionUnion _ ->
254248
let kind = Fable.OptionTest(unionCase.Name <> "None" && unionCase.Name <> "ValueNone")
255249
return Fable.Test(unionExpr, kind, r)
256250
| ListUnion _ ->
257251
let kind = Fable.ListTest(unionCase.CompiledName <> "Empty")
258252
return Fable.Test(unionExpr, kind, r)
259-
| StringEnum(_, rule) ->
260-
return makeEqOp r unionExpr (transformStringEnum rule unionCase) BinaryEqualStrict
261253
| DiscriminatedUnion(tdef,_) ->
262254
let tag = unionCaseTag tdef unionCase
263255
return Fable.Test(unionExpr, Fable.UnionCaseTest(tag), r)
@@ -678,18 +670,19 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr =
678670
| BasicPatterns.UnionCaseGet (unionExpr, fsType, unionCase, field) ->
679671
let r = makeRangeFrom fsExpr
680672
let! unionExpr = transformExpr com ctx unionExpr
681-
match fsType, unionCase with
682-
| ErasedUnionCase ->
683-
let index = unionCase.UnionCaseFields |> Seq.findIndex (fun x -> x.Name = field.Name)
684-
return Fable.Get(unionExpr, Fable.TupleIndex(index), makeType ctx.GenericArgs fsType, r)
685-
| ErasedUnion _ ->
686-
if unionCase.UnionCaseFields.Count = 1 then return unionExpr
687-
else
673+
match com, fsType, unionCase with
674+
| ErasedUnion(kind, _, _) ->
675+
let getByIndex offset =
688676
let index = unionCase.UnionCaseFields |> Seq.findIndex (fun x -> x.Name = field.Name)
689-
return Fable.Get(unionExpr, Fable.TupleIndex index, makeType ctx.GenericArgs fsType, r)
690-
| StringEnum _ ->
691-
return "StringEnum types cannot have fields"
692-
|> addErrorAndReturnNull com ctx.InlinePath r
677+
Fable.Get(unionExpr, Fable.TupleIndex(index + offset), makeType ctx.GenericArgs fsType, r)
678+
match kind with
679+
| EraseKind.AsValue -> return unionExpr
680+
| EraseKind.AsTuple -> return getByIndex 0
681+
| EraseKind.AsNamedTuple _ ->
682+
if unionCase.UnionCaseFields.Count = 0 then
683+
return "StringEnum types cannot have fields" |> addErrorAndReturnNull com ctx.InlinePath r
684+
else
685+
return getByIndex 1
693686
| OptionUnion t ->
694687
return Fable.Get(unionExpr, Fable.OptionValue, makeType ctx.GenericArgs t, r)
695688
| ListUnion t ->

src/Fable.Transforms/Global/Compiler.fs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ module Literals =
55

66
type CompilerOptionsHelper =
77
static member DefaultExtension = ".fs.js"
8-
static member Make(?typedArrays,
8+
static member Make(?eraseUnions,
9+
?typedArrays,
910
?typescript,
1011
?define,
1112
?optimizeFSharpAst,
@@ -17,6 +18,7 @@ type CompilerOptionsHelper =
1718
{ new CompilerOptions with
1819
member _.Define = define
1920
member _.DebugMode = isDebug
21+
member _.EraseUnions = defaultArg eraseUnions false
2022
member _.Typescript = defaultArg typescript false
2123
member _.TypedArrays = defaultArg typedArrays true
2224
member _.OptimizeFSharpAst = defaultArg optimizeFSharpAst false

0 commit comments

Comments
 (0)