Skip to content

Commit

Permalink
[Rust] Added support for Dictionary/HashSet comparers
Browse files Browse the repository at this point in the history
  • Loading branch information
ncave committed Sep 19, 2024
1 parent 6d752a7 commit b487e2b
Show file tree
Hide file tree
Showing 13 changed files with 445 additions and 195 deletions.
2 changes: 2 additions & 0 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* [JS/TS] Add support for `OrdinalIgnoreCase` overload for `String.EndsWith` (#3892) (by @goswinr)
* [JS/TS] Add `uri.Port`, `uri.IsDefaultPort` (by @MangelMaxime)
* [Rust] Added support for Dictionary/HashSet comparers (by @ncave)
* [Rust] Updated support for interface object expressions (by @ncave)

### Changed

Expand Down
8 changes: 1 addition & 7 deletions src/Fable.Transforms/Dart/Fable2Dart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2182,13 +2182,7 @@ module Util =
: string list
=

let genParams =
(Set.empty, argTypes)
||> List.fold (fun genArgs t ->
(genArgs, FSharp2Fable.Util.getGenParamNames t)
||> List.fold (fun genArgs n -> Set.add n genArgs)
)
|> List.ofSeq
let genParams = argTypes |> FSharp2Fable.Util.getGenParamNames

let genParams =
match genParams, ctx.EntityAndMemberGenericParams with
Expand Down
11 changes: 6 additions & 5 deletions src/Fable.Transforms/FSharp2Fable.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2092,15 +2092,16 @@ module Util =

let getMemberGenArgs (memb: Fable.MemberFunctionOrValue) : Fable.Type list = getMemberGenParams memb |> List.map snd

let getTypeGenParams (typ: Fable.Type) : (string * Fable.Type) list =
let rec getTypeGenParams (typ: Fable.Type) : (string * Fable.Type) list =
let getGenParams (types: Fable.Type list) : (string * Fable.Type) list =
let rec findGenParams typ =
match typ with
| Fable.GenericParam(name, false, _) as t -> [ (name, t) ]
| t -> t.Generics |> List.collect getTypeGenParams
| t -> t.Generics |> List.collect findGenParams

getTypeGenParams typ |> List.distinctBy fst
types |> List.collect findGenParams |> List.distinctBy fst

let getGenParamNames (typ: Fable.Type) : string list = getTypeGenParams typ |> List.map fst
let getGenParamNames (types: Fable.Type list) : string list = getGenParams types |> List.map fst
let getGenParamTypes (types: Fable.Type list) : Fable.Type list = getGenParams types |> List.map snd

/// We can add a suffix to the entity name for special methods, like reflection declaration
let entityIdentWithSuffix (com: Compiler) (ent: Fable.EntityRef) suffix =
Expand Down
4 changes: 1 addition & 3 deletions src/Fable.Transforms/Fable2Babel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1271,9 +1271,7 @@ module Util =

let typeParams =
types
|> List.collect FSharp2Fable.Util.getTypeGenParams
|> List.distinctBy fst
|> List.map snd
|> FSharp2Fable.Util.getGenParamTypes
|> List.filter (fun typ ->
match typ with
| Fable.GenericParam(name = name) ->
Expand Down
4 changes: 2 additions & 2 deletions src/Fable.Transforms/Python/Fable2Python.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1342,12 +1342,12 @@ module Util =
let undefined _range : Expression = Expression.none

let getGenericTypeParams (types: Fable.Type list) =
types |> List.collect FSharp2Fable.Util.getGenParamNames |> Set.ofList
types |> FSharp2Fable.Util.getGenParamNames |> Set.ofList

// Returns type parameters that is used more than once
let getRepeatedGenericTypeParams ctx (types: Fable.Type list) =
types
|> List.collect FSharp2Fable.Util.getGenParamNames
|> FSharp2Fable.Util.getGenParamNames
|> List.append (ctx.ScopedTypeParams |> Set.toList)
|> List.countBy id
|> List.choose (fun (param, count) ->
Expand Down
69 changes: 37 additions & 32 deletions src/Fable.Transforms/Rust/Fable2Rust.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1353,8 +1353,7 @@ module Util =

let genParams =
argTypes @ [ body.Type ]
|> List.collect FSharp2Fable.Util.getTypeGenParams
|> List.distinctBy fst
|> FSharp2Fable.Util.getGenParams
|> List.filter (fst >> isLambdaOrGenArgNotInScope)
|> List.filter (fst >> isNotLambdaOrGenArgInScope)

Expand Down Expand Up @@ -2113,7 +2112,14 @@ module Util =
Path = Fable.CoreAssemblyName assemblyName
}

let transformObjectExpr (com: IRustCompiler) ctx typ (members: Fable.ObjectExprMember list) baseCall : Rust.Expr =
let transformObjectExpr
(com: IRustCompiler)
ctx
typ
(members: Fable.ObjectExprMember list)
(baseCall: Fable.Expr option)
: Rust.Expr
=
if members |> List.isEmpty then
mkUnitExpr () // object constructors sometimes generate this
else
Expand Down Expand Up @@ -2144,17 +2150,6 @@ module Util =

let fieldIdents = fieldsMap.Values |> Seq.toList

let decl: Fable.ClassDecl =
{
Name = entName
Entity = entRef
Constructor = None
BaseCall = baseCall
AttachedMembers = members
XmlDoc = None
Tags = []
}

let fields =
fieldIdents
|> List.map (fun ident ->
Expand All @@ -2165,16 +2160,22 @@ module Util =

let attrs = [ mkAttr "derive" (makeDerivedFrom com ent) ]
let generics = makeGenerics com ctx genArgs
let genParams = FSharp2Fable.Util.getGenParamTypes genArgs

let structItems =
if baseCall.IsSome then
[] // TODO: if base type is not an interface
else
[ mkStructItem attrs entName fields generics ]

let memberItems = transformClassMembers com ctx decl
let genArgsOpt = transformGenArgs com ctx genArgs
let path = makeFullNamePath entName genArgsOpt
let ctx = { ctx with ScopedEntityGenArgs = getEntityGenParamNames ent }

let memberItems =
members
|> List.map (fun decl -> decl, com.GetMember(decl.MemberRef))
|> List.filter (fun (d, m) -> m.DeclaringEntity = Some(entRef))
|> List.map (makeMemberItem com ctx false)
|> makeInterfaceTraitImpls com ctx entName genParams entRef genArgs

let exprFields =
fieldIdents
Expand All @@ -2188,8 +2189,9 @@ module Util =
match baseCall with
| Some fableExpr -> com.TransformExpr(ctx, fableExpr)
| None ->
let genArgsOpt = transformGenArgs com ctx genParams
let path = makeFullNamePath entName genArgsOpt
let expr = mkStructExpr path exprFields |> makeLrcPtrValue com ctx

makeInterfaceCast com ctx typ expr

let objStmt = objExpr |> mkExprStmt
Expand Down Expand Up @@ -2325,10 +2327,13 @@ module Util =
let args = transformCallArgs com ctx callArgs callInfo.SignatureArgTypes argParams

let genArgsOpt =
if List.isEmpty callArgs then
transformGenArgs com ctx callInfo.GenericArgs
else
None
match membOpt with
| Some memb ->
if List.isEmpty callArgs && not (List.isEmpty memb.GenericParameters) then
transformGenArgs com ctx callInfo.GenericArgs
else
None
| _ -> None

match calleeExpr with
// mutable module values (transformed as function calls)
Expand Down Expand Up @@ -3387,7 +3392,7 @@ module Util =
let fieldGenParamSet =
ent.FSharpFields
|> List.map (fun field -> field.FieldType)
|> List.collect FSharp2Fable.Util.getGenParamNames
|> FSharp2Fable.Util.getGenParamNames
|> Set.ofList

let phantomGenParams =
Expand Down Expand Up @@ -4544,14 +4549,14 @@ module Util =

[ ctorItem ]

let makeInterfaceTraitImpls (com: IRustCompiler) ctx entName genArgs (ifc: Fable.DeclaredType) memberItems =
let makeInterfaceTraitImpls (com: IRustCompiler) ctx entName genArgs ifcEntRef ifcGenArgs memberItems =
let genArgsOpt = transformGenArgs com ctx genArgs
let traitBound = mkTypeTraitGenericBound [ entName ] genArgsOpt
let ty = mkTraitTy [ traitBound ]
let generics = makeGenerics com ctx genArgs

let ifcFullName = ifc.Entity |> getInterfaceImportName com ctx
let ifcGenArgsOpt = ifc.GenericArgs |> transformGenArgs com ctx
let ifcFullName = ifcEntRef |> getInterfaceImportName com ctx
let ifcGenArgsOpt = ifcGenArgs |> transformGenArgs com ctx

let path = makeFullNamePath ifcFullName ifcGenArgsOpt
let ofTrait = mkTraitRef path |> Some
Expand All @@ -4577,7 +4582,7 @@ module Util =

let entName =
if ent.IsInterface then
classDecl.Name // for interface object expressions
classDecl.Name
else
getEntityFullName com ctx entRef
|> Fable.Naming.splitLast
Expand All @@ -4593,9 +4598,9 @@ module Util =
let isNotExceptionMember (_m: Fable.MemberFunctionOrValue) = not (ent.IsFSharpExceptionDeclaration)

let isNonInterfaceMember (m: Fable.MemberFunctionOrValue) =
not (ent.IsInterface || m.IsOverrideOrExplicitInterfaceImplementation)
|| m.IsConstructor
|| (Set.contains m.CompiledName objectMemberNames)
m.IsConstructor
|| (not ent.IsInterface && not m.IsOverrideOrExplicitInterfaceImplementation)
|| (not ent.IsInterface && Set.contains m.CompiledName objectMemberNames)

let nonInterfaceMembers, interfaceMembers =
classDecl.AttachedMembers
Expand All @@ -4622,7 +4627,7 @@ module Util =

let displayTraitImpls =
if ent.IsInterface then
[] // for interface object expressions
[]
else
let hasToString = Set.contains "ToString" nonInterfaceMemberNames
makeDisplayTraitImpls com ctx self_ty genArgs hasToString
Expand Down Expand Up @@ -4655,7 +4660,7 @@ module Util =
if List.isEmpty memberItems then
[]
else
makeInterfaceTraitImpls com ctx entName genArgs ifc memberItems
makeInterfaceTraitImpls com ctx entName genArgs ifc.Entity ifc.GenericArgs memberItems
)

nonInterfaceImpls @ displayTraitImpls @ operatorTraitImpls @ interfaceTraitImpls
Expand Down
58 changes: 20 additions & 38 deletions src/Fable.Transforms/Rust/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ let operators (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr o
| "ToString", _ -> toString com ctx r args |> Some
| "CreateSequence", [ xs ] -> toSeq com t xs |> Some
| ("CreateDictionary" | "CreateReadOnlyDictionary"), [ arg ] ->
Helper.LibCall(com, "HashMap", "new_from_tup_array", t, [ toArray com t arg ])
Helper.LibCall(com, "HashMap", "new_from_tuple_array", t, [ toArray com t arg ])
|> Some
| "CreateSet", _ -> (genArg com ctx r 0 i.GenericArgs) |> makeSet com ctx r t args |> Some
// Ranges
Expand Down Expand Up @@ -2304,29 +2304,21 @@ let keyValuePairs (com: ICompiler) (ctx: Context) r t (i: CallInfo) thisArg args
let dictionaries (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match i.CompiledName, thisArg with
| ".ctor", None ->
match args with
match i.SignatureArgTypes with
| [] -> Helper.LibCall(com, "HashMap", "new_empty", t, args) |> Some
| [ ExprType(Number _) ] -> Helper.LibCall(com, "HashMap", "new_with_capacity", t, args) |> Some
| [ ExprType(IEnumerable) ] ->
let a = Helper.LibCall(com, "Seq", "toArray", t, args)
Helper.LibCall(com, "HashMap", "new_from_kvp_array", t, [ a ]) |> Some
// match i.SignatureArgTypes, args with
// | ([]|[Number _]), _ ->
// makeDictionary com ctx r t (makeArray Any []) |> Some
// | [IDictionary], [arg] ->
// makeDictionary com ctx r t arg |> Some
// | [IDictionary; IEqualityComparer], [arg; eqComp] ->
// makeComparerFromEqualityComparer eqComp
// |> makeDictionaryWithComparer com r t arg |> Some
// | [IEqualityComparer], [eqComp]
// | [Number _; IEqualityComparer], [_; eqComp] ->
// makeComparerFromEqualityComparer eqComp
// |> makeDictionaryWithComparer com r t (makeArray Any []) |> Some
// | _ -> None
| [ Number _ ] -> Helper.LibCall(com, "HashMap", "new_with_capacity", t, args) |> Some
| [ IEqualityComparer ] -> Helper.LibCall(com, "HashMap", "new_with_comparer", t, args) |> Some
| [ Number _; IEqualityComparer ] ->
Helper.LibCall(com, "HashMap", "new_with_capacity_comparer", t, args) |> Some
| [ IEnumerable ] -> Helper.LibCall(com, "HashMap", "new_from_enumerable", t, args) |> Some
| [ IEnumerable; IEqualityComparer ] ->
Helper.LibCall(com, "HashMap", "new_from_enumerable_comparer", t, args) |> Some
| [ IDictionary ] -> Helper.LibCall(com, "HashMap", "new_from_dictionary", t, args) |> Some
| [ IDictionary; IEqualityComparer ] ->
Helper.LibCall(com, "HashMap", "new_from_dictionary_comparer", t, args) |> Some
| _ -> None
| "GetEnumerator", Some c ->
let ar = Helper.LibCall(com, "HashMap", "entries", t, [ c ], [ c.Type ])

Helper.LibCall(com, "Seq", "Enumerable::ofArray", t, [ ar ], ?loc = r) |> Some
| "get_Item", Some c -> makeLibModuleCall com r t i "HashMap" "get" thisArg args |> Some
| "set_Item", Some c -> makeLibModuleCall com r t i "HashMap" "set" thisArg args |> Some
Expand All @@ -2337,28 +2329,18 @@ let dictionaries (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Exp
let hashSets (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match i.CompiledName, thisArg with
| ".ctor", None ->
match args with
match i.SignatureArgTypes with
| [] -> Helper.LibCall(com, "HashSet", "new_empty", t, args) |> Some
| [ ExprType(Number _) ] -> Helper.LibCall(com, "HashSet", "new_with_capacity", t, args) |> Some
| [ ExprTypeAs(IEnumerable, arg) ] ->
Helper.LibCall(com, "HashSet", "new_from_array", t, [ toArray com t arg ])
|> Some
// match i.SignatureArgTypes, args with
// | [], _ ->
// makeHashSet com ctx r t (makeArray Any []) |> Some
// | [IEnumerable], [arg] ->
// makeHashSet com ctx r t arg |> Some
// | [IEnumerable; IEqualityComparer], [arg; eqComp] ->
// makeComparerFromEqualityComparer eqComp
// |> makeHashSetWithComparer com r t arg |> Some
// | [IEqualityComparer], [eqComp] ->
// makeComparerFromEqualityComparer eqComp
// |> makeHashSetWithComparer com r t (makeArray Any []) |> Some
// | _ -> None
| [ Number _ ] -> Helper.LibCall(com, "HashSet", "new_with_capacity", t, args) |> Some
| [ IEqualityComparer ] -> Helper.LibCall(com, "HashSet", "new_with_comparer", t, args) |> Some
| [ Number _; IEqualityComparer ] ->
Helper.LibCall(com, "HashSet", "new_with_capacity_comparer", t, args) |> Some
| [ IEnumerable ] -> Helper.LibCall(com, "HashSet", "new_from_enumerable", t, args) |> Some
| [ IEnumerable; IEqualityComparer ] ->
Helper.LibCall(com, "HashSet", "new_from_enumerable_comparer", t, args) |> Some
| _ -> None
| "GetEnumerator", Some c ->
let ar = Helper.LibCall(com, "HashSet", "entries", t, [ c ])

Helper.LibCall(com, "Seq", "Enumerable::ofArray", t, [ ar ], ?loc = r) |> Some
| ("IsProperSubsetOf" | "IsProperSupersetOf" | "UnionWith" | "IntersectWith" | "ExceptWith" | "IsSubsetOf" | "IsSupersetOf" as meth),
Some c ->
Expand Down
Loading

0 comments on commit b487e2b

Please sign in to comment.