Skip to content

Commit be37563

Browse files
Add tag argument to Erase unions
1 parent 07d0311 commit be37563

File tree

3 files changed

+43
-26
lines changed

3 files changed

+43
-26
lines changed

src/Fable.Core/Fable.Core.Types.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type AttachMembersAttribute() =
2929
type EraseAttribute() =
3030
inherit Attribute()
3131
new (caseRules: CaseRules) = EraseAttribute()
32+
new (tag: bool) = EraseAttribute()
3233

3334
/// Used for "tagged" union types, which is commonly used in TypeScript.
3435
type TypeScriptTaggedUnionAttribute(tagName: string, caseRules: CaseRules) =

src/Fable.Transforms/FSharp2Fable.Util.fs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -733,19 +733,14 @@ module Helpers =
733733
type UnionPattern =
734734
| OptionUnion of FSharpType * isStruct: bool
735735
| ListUnion of FSharpType
736-
| ErasedUnion of FSharpEntity * IList<FSharpType> * CaseRules
736+
| ErasedUnion of FSharpEntity * IList<FSharpType> * CaseRules * tag: bool
737737
| ErasedUnionCase
738738
| TypeScriptTaggedUnion of FSharpEntity * IList<FSharpType> * tagName:string * CaseRules
739739
| StringEnum of FSharpEntity * CaseRules
740740
| DiscriminatedUnion of FSharpEntity * IList<FSharpType>
741741

742742
let getUnionPattern (typ: FSharpType) (unionCase: FSharpUnionCase) : UnionPattern =
743743
let typ = nonAbbreviatedType typ
744-
let getCaseRule (att: FSharpAttribute) =
745-
match Seq.tryHead att.ConstructorArguments with
746-
| Some(_, (:? int as rule)) -> enum<CaseRules>(rule)
747-
| _ -> CaseRules.LowerFirst
748-
749744
unionCase.Attributes |> Seq.tryPick (fun att ->
750745
match att.AttributeType.TryFullName with
751746
| Some Atts.erase -> Some ErasedUnionCase
@@ -761,8 +756,20 @@ module Helpers =
761756
| _ ->
762757
tdef.Attributes |> Seq.tryPick (fun att ->
763758
match att.AttributeType.TryFullName with
764-
| Some Atts.erase -> Some (ErasedUnion(tdef, typ.GenericArguments, getCaseRule att))
765-
| Some Atts.stringEnum -> Some (StringEnum(tdef, getCaseRule att))
759+
| Some Atts.erase ->
760+
let caseRule, tag =
761+
match Seq.tryHead att.ConstructorArguments with
762+
| Some(_, (:? int as rule)) -> enum<CaseRules>(rule), false
763+
| Some(_, (:? bool as tag)) ->
764+
if tag then CaseRules.None, true else CaseRules.LowerFirst, false
765+
| _ -> CaseRules.LowerFirst, false
766+
Some (ErasedUnion(tdef, typ.GenericArguments, caseRule, tag))
767+
| Some Atts.stringEnum ->
768+
let caseRule =
769+
match Seq.tryHead att.ConstructorArguments with
770+
| Some(_, (:? int as rule)) -> enum<CaseRules>(rule)
771+
| _ -> CaseRules.LowerFirst
772+
Some (StringEnum(tdef, caseRule))
766773
| Some Atts.tsTaggedUnion ->
767774
match Seq.tryItem 0 att.ConstructorArguments, Seq.tryItem 1 att.ConstructorArguments with
768775
| Some (_, (:? string as name)), None ->

src/Fable.Transforms/FSharp2Fable.fs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,19 @@ let private transformNewUnion com ctx r fsType (unionCase: FSharpUnionCase) (arg
4646
match getUnionPattern fsType unionCase with
4747
| ErasedUnionCase ->
4848
makeTuple r false argExprs
49-
| ErasedUnion(tdef, _genArgs, rule) ->
50-
match argExprs with
51-
| [] -> transformStringEnum rule unionCase
52-
| [argExpr] -> argExpr
53-
| _ when tdef.UnionCases.Count > 1 ->
54-
"Erased unions with multiple cases must have one single field: " + (getFsTypeFullName fsType)
55-
|> addErrorAndReturnNull com ctx.InlinePath r
56-
| argExprs -> makeTuple r false argExprs
49+
// TODO: Wrap erased unions in type cast so type info is not lost
50+
| ErasedUnion(tdef, _genArgs, rule, tag) ->
51+
if tag then
52+
(transformStringEnum rule unionCase)::argExprs |> makeTuple r false
53+
else
54+
match argExprs with
55+
| [] -> transformStringEnum rule unionCase
56+
| [argExpr] -> argExpr
57+
| _ when tdef.UnionCases.Count > 1 ->
58+
$"Erased unions with multiple fields must have one single case: {getFsTypeFullName fsType}. " +
59+
"To allow multiple cases pass tag argument, e.g.: [<Erase(tag=true)>]"
60+
|> addErrorAndReturnNull com ctx.InlinePath r
61+
| argExprs -> makeTuple r false argExprs
5762
| TypeScriptTaggedUnion _ ->
5863
match argExprs with
5964
| [argExpr] -> argExpr
@@ -326,10 +331,14 @@ let private transformUnionCaseTest (com: IFableCompiler) (ctx: Context) r
326331
| ErasedUnionCase ->
327332
return "Cannot test erased union cases"
328333
|> addErrorAndReturnNull com ctx.InlinePath r
329-
| ErasedUnion(tdef, genArgs, rule) ->
330-
match unionCase.Fields.Count with
331-
| 0 -> return makeEqOp r unionExpr (transformStringEnum rule unionCase) BinaryEqual
332-
| 1 ->
334+
| ErasedUnion(tdef, genArgs, rule, tag) ->
335+
match tag, unionCase.Fields.Count with
336+
| true, _ ->
337+
let tagName = transformStringEnum rule unionCase
338+
let tagExpr = Fable.Get(unionExpr, Fable.TupleIndex 0, Fable.String, None)
339+
return makeEqOp r tagExpr tagName BinaryEqual
340+
| false, 0 -> return makeEqOp r unionExpr (transformStringEnum rule unionCase) BinaryEqual
341+
| false, 1 ->
333342
let fi = unionCase.Fields[0]
334343
let typ =
335344
if fi.FieldType.IsGenericParameter then
@@ -341,7 +350,7 @@ let private transformUnionCaseTest (com: IFableCompiler) (ctx: Context) r
341350
else fi.FieldType
342351
let kind = makeType ctx.GenericArgs typ |> Fable.TypeTest
343352
return Fable.Test(unionExpr, kind, r)
344-
| _ ->
353+
| false, _ ->
345354
return "Erased unions with multiple cases cannot have more than one field: " + (getFsTypeFullName fsType)
346355
|> addErrorAndReturnNull com ctx.InlinePath r
347356
| TypeScriptTaggedUnion (_, _, tagName, rule) ->
@@ -863,16 +872,16 @@ let private transformExpr (com: IFableCompiler) (ctx: Context) fsExpr =
863872
return Fable.Get(tupleExpr, Fable.TupleIndex tupleElemIndex, typ, makeRangeFrom fsExpr)
864873

865874
| FSharpExprPatterns.UnionCaseGet (IgnoreAddressOf unionExpr, fsType, unionCase, field) ->
875+
let getIndex() = unionCase.Fields |> Seq.findIndex (fun x -> x.Name = field.Name)
866876
let r = makeRangeFrom fsExpr
867877
let! unionExpr = transformExpr com ctx unionExpr
868878
match getUnionPattern fsType unionCase with
869879
| ErasedUnionCase ->
870-
let index = unionCase.Fields |> Seq.findIndex (fun x -> x.Name = field.Name)
871-
return Fable.Get(unionExpr, Fable.TupleIndex(index), makeType ctx.GenericArgs fsType, r)
872-
| ErasedUnion _ ->
873-
if unionCase.Fields.Count = 1 then return unionExpr
880+
return Fable.Get(unionExpr, Fable.TupleIndex(getIndex()), makeType ctx.GenericArgs fsType, r)
881+
| ErasedUnion(_tdef, _genArgs, _rule, tag) ->
882+
if not tag && unionCase.Fields.Count = 1 then return unionExpr
874883
else
875-
let index = unionCase.Fields |> Seq.findIndex (fun x -> x.Name = field.Name)
884+
let index = if tag then getIndex() + 1 else getIndex()
876885
return Fable.Get(unionExpr, Fable.TupleIndex index, makeType ctx.GenericArgs fsType, r)
877886
| TypeScriptTaggedUnion _ ->
878887
if unionCase.Fields.Count = 1 then return unionExpr

0 commit comments

Comments
 (0)