diff --git a/.vscode/launch.json b/.vscode/launch.json index 455046b4e4..dde10cbb32 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -193,18 +193,12 @@ "preLaunchTask": "build", "program": "${workspaceFolder}/src/Fable.Cli/bin/Debug/net6.0/fable.dll", "args": [ - "--", "src/fable-library-dart", - "--outDir", - "temp/fable-library-dart", - "--fableLib", - "./temp/fable-library-dart", - "--lang", - "dart", - "--exclude", - "Fable.Core", - "--define", - "FABLE_LIBRARY", + "--outDir", "temp/fable-library-dart", + "--fableLib", "./temp/fable-library-dart", + "--exclude", "Fable.Core", + "--define", "FABLE_LIBRARY", + "--lang", "Dart", "--noCache" ] }, @@ -216,21 +210,14 @@ "program": "${workspaceFolder}/src/Fable.Cli/bin/Debug/net6.0/fable.dll", "args": [ "src/fable-library-ts", - "--outDir", - "temp/fable-library-ts", - "--fableLib", - "./temp/fable-library-ts", - "--lang", - "typescript", - "--exclude", - "Fable.Core", - "--define", - "FABLE_LIBRARY", - "--noCache", - "--typedArrays", - "false", - "--define", - "FX_NO_BIGINT" + "--outDir", "temp/fable-library-ts", + "--fableLib", "./temp/fable-library-ts", + "--exclude", "Fable.Core", + "--define", "FABLE_LIBRARY", + "--define", "FX_NO_BIGINT", + "--typedArrays", "false", + "--lang", "TypeScript", + "--noCache" ] }, { @@ -241,16 +228,11 @@ "program": "${workspaceFolder}/src/Fable.Cli/bin/Debug/net6.0/fable.dll", "args": [ "src/fable-library-py/fable_library", - "--outDir", - "temp/fable-library-py/fable_library", - "--fableLib", - ".", - "--lang", - "python", - "--exclude", - "Fable.Core", - "--define", - "FABLE_LIBRARY", + "--outDir", "temp/fable-library-py/fable_library", + "--fableLib", ".", + "--exclude", "Fable.Core", + "--define", "FABLE_LIBRARY", + "--lang", "Python", "--noCache" ] }, @@ -262,29 +244,34 @@ "program": "${workspaceFolder}/src/Fable.Cli/bin/Debug/net6.0/fable.dll", "args": [ "src/fable-library-rust/src", - "--outDir", - "temp/fable-library-rust/src", - "--fableLib", - ".", - "--lang", - "rust", - "--exclude", - "Fable.Core", - "--define", - "FABLE_LIBRARY", - "--noCache" + "--outDir", "temp/fable-library-rust/src", + "--fableLib", ".", + "--exclude", "Fable.Core", + "--define", "FABLE_LIBRARY", + "--lang", "Rust", + "--noCache", + "--noParallelTypeCheck", + "--test:MSBuildCracker" ] }, { - "name": "Fable.Cli on ../fable-test", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceRoot}/src/Fable.Cli/bin/Debug/net6.0/fable.dll", - "args": ["--outDir", "${workspaceRoot}/../fable-test", "--fableLib", "${workspaceRoot}/temp/fable-library-rust", "--exclude", "Fable.Core", "--lang", "Rust", "--noCache", "--noParallelTypeCheck", "--test:MSBuildCracker"], - "cwd": "${workspaceRoot}/../fable-test", - "stopAtEntry": false, - "console": "internalConsole" + "name": "Fable.Cli on ../fable-test", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceRoot}/src/Fable.Cli/bin/Debug/net6.0/fable.dll", + "args": [ + "--outDir", "${workspaceRoot}/../fable-test", + "--fableLib", "${workspaceRoot}/temp/fable-library-rust", + "--exclude", "Fable.Core", + "--lang", "Rust", + "--noCache", + "--noParallelTypeCheck", + "--test:MSBuildCracker" + ], + "cwd": "${workspaceRoot}/../fable-test", + "stopAtEntry": false, + "console": "internalConsole" } ] -} +} \ No newline at end of file diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 5af4921d8a..a3ab53261a 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -13,6 +13,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 diff --git a/src/Fable.Cli/Main.fs b/src/Fable.Cli/Main.fs index 4b5177228f..a85fb92dd3 100644 --- a/src/Fable.Cli/Main.fs +++ b/src/Fable.Cli/Main.fs @@ -478,7 +478,7 @@ type FableCompilerState = and FableCompiler(checker: InteractiveChecker, projCracked: ProjectCracked, fableProj: Project) = let agent = MailboxProcessor.Start(fun agent -> - let startInThreadPool toMsg work = + let postTo toMsg work = async { try let! result = work () @@ -486,12 +486,22 @@ and FableCompiler(checker: InteractiveChecker, projCracked: ProjectCracked, fabl with e -> UnexpectedError e |> agent.Post } - |> Async.Start + + let startInThreadPool toMsg work = postTo toMsg work |> Async.Start + + let runSynchronously toMsg work = + postTo toMsg work |> Async.RunSynchronously let fableCompile state fileName = let fableProj = state.FableProj - startInThreadPool + let runner = + // for Rust, sequential compilation captures all imports and namespaces + match projCracked.CliArgs.CompilerOptions.Language with + | Rust -> runSynchronously // sequential file compilation + | _ -> startInThreadPool // parallel file compilation + + runner FableFileCompiled (fun () -> async { @@ -574,7 +584,6 @@ and FableCompiler(checker: InteractiveChecker, projCracked: ProjectCracked, fabl // Print F# AST to file if projCracked.CliArgs.PrintAst then let outPath = getOutPath projCracked.CliArgs state.PathResolver file.FileName - let outDir = IO.Path.GetDirectoryName(outPath) Printers.printAst outDir [ file ] @@ -586,7 +595,6 @@ and FableCompiler(checker: InteractiveChecker, projCracked: ProjectCracked, fabl state else let state = { state with FableProj = state.FableProj.Update([ file ]) } - fableCompile state fileName return! loop state diff --git a/src/Fable.Transforms/Dart/Fable2Dart.fs b/src/Fable.Transforms/Dart/Fable2Dart.fs index d3002b88c3..ecf5320df4 100644 --- a/src/Fable.Transforms/Dart/Fable2Dart.fs +++ b/src/Fable.Transforms/Dart/Fable2Dart.fs @@ -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 diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index 68a847dd38..11fbe48403 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -354,6 +354,8 @@ type FsEnt(maybeAbbrevEnt: FSharpEntity) = | None -> ent.LogicalName static member Ref(ent: FSharpEntity) : Fable.EntityRef = + let ent = Helpers.nonAbbreviatedDefinition ent + let path = match ent.Assembly.FileName with | Some asmPath -> @@ -2092,15 +2094,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 = @@ -2710,6 +2713,13 @@ module Util = ) |> snd + let getInterfaceMembers (com: Compiler) (ent: Fable.Entity) = + ent.AllInterfaces + |> Seq.collect (fun ifc -> + let ifcEnt = com.GetEntity(ifc.Entity) + ifcEnt.MembersFunctionsAndValues |> Seq.map (fun memb -> ifc, memb) + ) + let hasInterface fullName (ent: Fable.Entity) = ent.AllInterfaces |> Seq.exists (fun ifc -> ifc.Entity.FullName = fullName) diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index a3db44c3c4..eb3743c2ce 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -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) -> diff --git a/src/Fable.Transforms/Global/Naming.fs b/src/Fable.Transforms/Global/Naming.fs index 741d378e36..616334470e 100644 --- a/src/Fable.Transforms/Global/Naming.fs +++ b/src/Fable.Transforms/Global/Naming.fs @@ -127,8 +127,16 @@ module Naming = else s.Substring(i1 + 1, i2 - i1 - 1) + let splitFirstBy (sep: string) (s: string) = + let i = s.IndexOf(sep, StringComparison.Ordinal) + + if i < 0 then + s, "" + else + s.Substring(0, i), s.Substring(i + sep.Length) + let splitLastBy (sep: string) (s: string) = - let i = s.LastIndexOf(sep) + let i = s.LastIndexOf(sep, StringComparison.Ordinal) if i < 0 then "", s diff --git a/src/Fable.Transforms/Python/Fable2Python.fs b/src/Fable.Transforms/Python/Fable2Python.fs index 614d3b83d4..997f1c53fd 100644 --- a/src/Fable.Transforms/Python/Fable2Python.fs +++ b/src/Fable.Transforms/Python/Fable2Python.fs @@ -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) -> diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index 4c45779e1d..bad7b91857 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -828,57 +828,19 @@ module TypeInfo = | None -> match entRef.Path with | Fable.AssemblyPath _ - | Fable.CoreAssemblyName _ when not (Util.isFableLibrary com) -> + | Fable.CoreAssemblyName _ -> //TODO: perhaps only import from library if it's already implemented BCL class - let importName = com.GetImportName(ctx, entRef.FullName, "fable_library_rust", None) + let importPath = "fable_library_rust" + let importName = com.GetImportName(ctx, entRef.FullName, importPath, None) importName - | _ when (Util.isFableLibrary com) -> "crate::" + entRef.FullName | _ -> entRef.FullName - let declaredInterfaces = - Set.ofList - [ - Types.icollection - Types.icollectionGeneric - Types.idictionary - Types.ireadonlydictionary - Types.idisposable - Types.iformattable - Types.iformatProvider - Types.icomparer - Types.icomparerGeneric - Types.iequalityComparer - Types.iequalityComparerGeneric - Types.ienumerable - Types.ienumerableGeneric - Types.ienumerator - Types.ienumeratorGeneric - Types.iequatableGeneric - Types.icomparable - Types.icomparableGeneric - Types.iStructuralEquatable - Types.iStructuralComparable - ] - - let isDeclaredInterface fullName = - Set.contains fullName declaredInterfaces - - let getInterfaceImportName (com: IRustCompiler) ctx (entRef: Fable.EntityRef) = - if isDeclaredInterface entRef.FullName then - getLibraryImportName com ctx "Interfaces" entRef.FullName - else - getEntityFullName com ctx entRef - // let selector = "crate::" + entRef.FullName - // let path = "" - // com.GetImportName(ctx, selector, path, None) - let tryFindInterface (com: IRustCompiler) fullName (entRef: Fable.EntityRef) : Fable.DeclaredType option = let ent = com.GetEntity(entRef) - ent.AllInterfaces |> Seq.tryFind (fun ifc -> ifc.Entity.FullName = fullName) let transformInterfaceType com ctx (entRef: Fable.EntityRef) genArgs : Rust.Ty = - let nameParts = getInterfaceImportName com ctx entRef |> splitNameParts + let nameParts = getEntityFullName com ctx entRef |> splitNameParts let genArgsOpt = transformGenArgs com ctx genArgs let traitBound = mkTypeTraitGenericBound nameParts genArgsOpt mkDynTraitTy [ traitBound ] @@ -1353,8 +1315,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) @@ -2113,7 +2074,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 @@ -2144,17 +2112,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 -> @@ -2165,6 +2122,7 @@ 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 @@ -2172,9 +2130,15 @@ module Util = 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) -> isInterfaceMember com entRef m) + |> List.distinctBy (fun (d, m) -> Fable.Naming.splitLast m.CompiledName) + |> List.map (makeMemberItem com ctx false) + |> makeInterfaceTraitImpls com ctx entName genParams entRef genArgs let exprFields = fieldIdents @@ -2188,8 +2152,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 @@ -2325,10 +2290,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) @@ -2415,7 +2383,7 @@ module Util = // match calleeExpr.Type with // | IsNonErasedInterface com (entRef, genArgs) -> // // interface instance call (using fully qualified syntax) - // let ifcName = getInterfaceImportName com ctx entRef + // let ifcName = getEntityFullName com ctx entRef // let parts = (ifcName + "::" + membName) |> splitNameParts // (callee |> makeAsRef) :: args |> makeCall parts None // | _ -> @@ -3387,7 +3355,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 = @@ -3715,7 +3683,7 @@ module Util = let ent = com.GetEntity(entRef) if ent.IsInterface then - let nameParts = getInterfaceImportName com ctx entRef |> splitNameParts + let nameParts = getEntityFullName com ctx entRef |> splitNameParts let genArgsOpt = transformGenArgs com ctx genArgs let traitBound = mkTypeTraitGenericBound nameParts genArgsOpt [ traitBound ] @@ -3902,7 +3870,6 @@ module Util = let macroItem = mkMacroItem attrs macroName [ expr ] [ macroItem ] - let transformExtensionMethod (com: IRustCompiler) ctx (memb: Fable.MemberFunctionOrValue) (decl: Fable.MemberDecl) = let argTypes = decl.Args |> List.map (fun arg -> arg.Type) @@ -4082,17 +4049,24 @@ module Util = let fnItem = mkFnAssocItem attrs name fnKind fnItem - let getInterfaceMemberNames (com: IRustCompiler) (entRef: Fable.EntityRef) = + let isInterfaceMember (com: IRustCompiler) (entRef: Fable.EntityRef) (memb: Fable.MemberFunctionOrValue) = let ent = com.GetEntity(entRef) assert (ent.IsInterface) - ent.AllInterfaces - |> Seq.collect (fun i -> - let e = com.GetEntity(i.Entity) - e.MembersFunctionsAndValues - ) - |> Seq.map (fun m -> m.CompiledName) - |> Set.ofSeq + match memb.DeclaringEntity with + | Some declEntRef -> + let declEnt = com.GetEntity(declEntRef) + + if declEnt.IsInterface then + ent.AllInterfaces |> Seq.exists (fun ifc -> ifc.Entity = declEntRef) + else + // if declaring entity is not an interface, the interface is in the member.CompiledName + let ifcName, membName = Fable.Naming.splitLastBy "." memb.CompiledName + let ifcName, _ = Fable.Naming.splitFirstBy "<" ifcName // trim the generics + + ent.AllInterfaces + |> Seq.exists (fun ifc -> ifc.Entity.FullName.StartsWith(ifcName)) + | None -> false let makeDerivedFrom com (ent: Fable.Entity) = let isCopyable = ent |> isCopyableEntity com Set.empty @@ -4284,59 +4258,56 @@ module Util = let ctor = { ctor with Body = body } let memb = com.GetMember(ctor.MemberRef) - let fnItem = transformAssocMember com ctx memb ctor.Name ctor.Args ctor.Body - let fnItem = fnItem |> memberAssocItemWithVis com ctx memb fnItem let makeInterfaceItems (com: IRustCompiler) ctx hasBody typeName (ent: Fable.Entity) = - ent.AllInterfaces - |> Seq.collect (fun ifc -> - let ifcTyp = Fable.DeclaredType(ifc.Entity, ifc.GenericArgs) - let ifcEnt = com.GetEntity(ifc.Entity) + assert (ent.IsInterface) - ifcEnt.MembersFunctionsAndValues - |> Seq.filter (fun memb -> memb.IsDispatchSlot) - |> Seq.distinctBy (fun memb -> memb.CompiledName) - |> Seq.map (fun memb -> - let thisArg = { makeTypedIdent ifcTyp "this" with IsThisArgument = true } + ent + |> FSharp2Fable.Util.getInterfaceMembers com + |> Seq.filter (fun (ifc, memb) -> memb.IsDispatchSlot) // TODO: is that needed? + |> Seq.distinctBy (fun (ifc, memb) -> Fable.Naming.splitLast memb.CompiledName) // skip inherited overwrites + |> Seq.map (fun (ifc, memb) -> + let ifcEnt = com.GetEntity(ifc.Entity) + let ifcTyp = Fable.DeclaredType(ifc.Entity, ifc.GenericArgs) + let thisArg = { makeTypedIdent ifcTyp "this" with IsThisArgument = true } - let membName = memb.CompiledName + let membName = memb.CompiledName - let memberArgs = - memb.CurriedParameterGroups - |> List.collect id - |> List.mapi (fun i p -> - let name = defaultArg p.Name $"arg{i}" - makeTypedIdent p.Type name - ) + let memberArgs = + memb.CurriedParameterGroups + |> List.collect id + |> List.mapi (fun i p -> + let name = defaultArg p.Name $"arg{i}" + makeTypedIdent p.Type name + ) - let args = - if memb.IsInstance then - thisArg :: memberArgs - else - memberArgs - - let bodyOpt = - if hasBody then - let args = memberArgs |> List.map (transformIdent com ctx None) - - let body = - if memb.IsInstance then - // let thisExpr = makeThis com ctx None ifcTyp - let thisExpr = mkGenericPathExpr [ rawIdent "self" ] None - let callee = thisExpr |> mkDerefExpr |> mkDerefExpr - mkMethodCallExpr membName None callee args - else - makeCall [ typeName; membName ] None args + let args = + if memb.IsInstance then + thisArg :: memberArgs + else + memberArgs + + let bodyOpt = + if hasBody then + let args = memberArgs |> List.map (transformIdent com ctx None) + + let body = + if memb.IsInstance then + // let thisExpr = makeThis com ctx None ifcTyp + let thisExpr = mkGenericPathExpr [ rawIdent "self" ] None + let callee = thisExpr |> mkDerefExpr |> mkDerefExpr + mkMethodCallExpr membName None callee args + else + makeCall [ typeName; membName ] None args - [ mkExprStmt body ] |> mkBlock |> Some - else - None + [ mkExprStmt body ] |> mkBlock |> Some + else + None - makeAssocMemberItem com ctx memb args bodyOpt - ) + makeAssocMemberItem com ctx memb args bodyOpt ) let transformInterface (com: IRustCompiler) ctx (ent: Fable.Entity) (decl: Fable.ClassDecl) = @@ -4511,9 +4482,7 @@ module Util = { ctx with UsedNames = { ctx.UsedNames with CurrentDeclarationScope = HashSet usedNames } } let result = f ctx - ctx.UsedNames.DeclarationScopes.UnionWith(ctx.UsedNames.CurrentDeclarationScope) - result let makeMemberItem (com: IRustCompiler) ctx withVis (decl: Fable.MemberDecl, memb: Fable.MemberFunctionOrValue) = @@ -4544,14 +4513,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 |> getEntityFullName com ctx + let ifcGenArgsOpt = ifcGenArgs |> transformGenArgs com ctx let path = makeFullNamePath ifcFullName ifcGenArgsOpt let ofTrait = mkTraitRef path |> Some @@ -4571,13 +4540,21 @@ module Util = let ignoredInterfaceNames = set [ Types.ienumerable; Types.ienumerator ] + let getAllInterfaces (ent: Fable.Entity) : Fable.DeclaredType list = + ent.AllInterfaces + |> Seq.filter (fun ifc -> + // throws out anything on the ignored interfaces list + not (Set.contains ifc.Entity.FullName ignoredInterfaceNames) + ) + |> Seq.toList + let transformClassMembers (com: IRustCompiler) ctx (classDecl: Fable.ClassDecl) = let entRef = classDecl.Entity let ent = com.GetEntity(entRef) let entName = if ent.IsInterface then - classDecl.Name // for interface object expressions + classDecl.Name else getEntityFullName com ctx entRef |> Fable.Naming.splitLast @@ -4593,9 +4570,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 @@ -4622,7 +4599,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 @@ -4631,31 +4608,19 @@ module Util = nonInterfaceMembers |> List.choose (makeOpTraitImpls com ctx ent entType self_ty genArgTys) - let interfaces = - ent.AllInterfaces - |> Seq.filter (fun ifc -> - // throws out anything on the ignored interfaces list - not (Set.contains ifc.Entity.FullName ignoredInterfaceNames) - ) - |> Seq.toList - let interfaceTraitImpls = - interfaces + getAllInterfaces ent |> List.collect (fun ifc -> - let ifcMemberNames = ifc.Entity |> getInterfaceMemberNames com - let memberItems = interfaceMembers - |> List.filter (fun (d, m) -> - //TODO: match the interface entity too, not just the member name - Set.contains d.Name ifcMemberNames - ) + |> List.filter (fun (d, m) -> isInterfaceMember com ifc.Entity m) + |> List.distinctBy (fun (d, m) -> Fable.Naming.splitLast m.CompiledName) |> List.map (makeMemberItem com ctx false) 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 @@ -4666,10 +4631,7 @@ module Util = if ent.IsFSharpAbbreviation then transformAbbrev com ctx ent decl elif ent.IsInterface then - if isDeclaredInterface ent.FullName then - [] - else - transformInterface com ctx ent decl + transformInterface com ctx ent decl else let entityItem = if ent.IsFSharpUnion then @@ -4949,11 +4911,10 @@ module Compiler = let isMacro = selector.EndsWith("!", StringComparison.Ordinal) let selector = selector |> Fable.Naming.replaceSuffix "!" "" + let selector = selector.Replace(".", "::").Replace("`", "_") let path = fixFileExtension self path let cacheKey = - let selector = selector.Replace(".", "::").Replace("`", "_") - if (isFableLibraryPath self path) then "fable_library_rust::" + selector elif path.Length = 0 then @@ -4981,7 +4942,11 @@ module Compiler = Depths = [ ctx.ModuleDepth ] } // add import module to a global list (across files) - if path.Length > 0 && not (isFableLibraryPath self path) then + if + path.Length > 0 + && path <> "fable_library_rust" + && not (isFableLibraryPath self path) + then importModules.TryAdd(modulePath, true) |> ignore imports.Add(cacheKey, import) diff --git a/src/Fable.Transforms/Rust/Replacements.fs b/src/Fable.Transforms/Rust/Replacements.fs index 3f3f9a7945..7067904f6e 100644 --- a/src/Fable.Transforms/Rust/Replacements.fs +++ b/src/Fable.Transforms/Rust/Replacements.fs @@ -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 @@ -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 @@ -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 -> diff --git a/src/fable-library-rust/src/Fable.Library.Rust.fsproj b/src/fable-library-rust/src/Fable.Library.Rust.fsproj index 8252c7e825..1365dbd308 100644 --- a/src/fable-library-rust/src/Fable.Library.Rust.fsproj +++ b/src/fable-library-rust/src/Fable.Library.Rust.fsproj @@ -7,11 +7,6 @@ - - - - - @@ -22,6 +17,11 @@ + + + + + diff --git a/src/fable-library-rust/src/HashMap.rs b/src/fable-library-rust/src/HashMap.rs index bfe3a2b907..c2dee0058f 100644 --- a/src/fable-library-rust/src/HashMap.rs +++ b/src/fable-library-rust/src/HashMap.rs @@ -9,77 +9,181 @@ pub mod HashMap_ { #[cfg(not(feature = "no_std"))] use std::collections; - use crate::Native_::{mkRefMut, Lrc, LrcPtr, MutCell, Vec}; + use crate::System::Collections::Generic::IEqualityComparer_1; use crate::NativeArray_::{array_from, Array}; - type MutHashMap = MutCell>; + use crate::Native_::{mkRefMut, seq_to_iter, HashKey, Lrc, LrcPtr, MutCell, Seq, Vec}; use core::fmt::{Debug, Display, Formatter, Result}; - use core::hash::Hash; + use core::hash::{Hash, Hasher}; - #[repr(transparent)] - #[derive(Clone, Debug, Default)] //, PartialEq, PartialOrd, Eq, Hash, Ord)] - pub struct HashMap(Lrc>); + type MutHashMap = MutCell, V>>; + + #[derive(Clone)] //, Debug, Default, PartialEq, PartialOrd, Eq, Hash, Ord)] + pub struct HashMap { + hash_map: Lrc>, + comparer: Option>>, + } + + impl Default for HashMap { + fn default() -> HashMap { + new_empty() + } + } impl core::ops::Deref for HashMap { type Target = Lrc>; fn deref(&self) -> &Self::Target { - &self.0 + &self.hash_map + } + } + + impl Debug for HashMap { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}", self.hash_map) //TODO: } } impl Display for HashMap { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{:?}", self.0) //TODO: + write!(f, "{:?}", self.hash_map) //TODO: + } + } + + fn from_iter>( + iter: I, + comparer: Option>>, + ) -> HashMap + where + K: Clone + Hash + PartialEq + 'static, + { + let it = iter.map(|(k, v)| { + let key = HashKey::new(k, comparer.clone()); + (key, v) + }); + HashMap { + hash_map: mkRefMut(collections::HashMap::from_iter(it)), + comparer: comparer.clone(), } } + fn to_iter(map: &HashMap) -> impl Iterator + '_ { + map.iter().map(|(k, v)| (k.key.clone(), v.clone())) + } + pub fn new_empty() -> HashMap { - HashMap(mkRefMut(collections::HashMap::new())) + HashMap { + hash_map: mkRefMut(collections::HashMap::new()), + comparer: None, + } } pub fn new_with_capacity(capacity: i32) -> HashMap { - HashMap(mkRefMut(collections::HashMap::with_capacity( - capacity as usize, - ))) + HashMap { + hash_map: mkRefMut(collections::HashMap::with_capacity(capacity as usize)), + comparer: None, + } } - pub fn new_from_tup_array(a: Array>) -> HashMap { - let it = a.iter().map(|tup| tup.as_ref().clone()); - HashMap(mkRefMut(collections::HashMap::from_iter(it))) + pub fn new_with_comparer( + comparer: LrcPtr>, + ) -> HashMap { + HashMap { + hash_map: mkRefMut(collections::HashMap::new()), + comparer: Some(comparer), + } + } + + pub fn new_with_capacity_comparer( + capacity: i32, + comparer: LrcPtr>, + ) -> HashMap { + HashMap { + hash_map: mkRefMut(collections::HashMap::with_capacity(capacity as usize)), + comparer: Some(comparer), + } + } + + pub fn new_from_enumerable(seq: Seq<(K, V)>) -> HashMap + where + K: Clone + Hash + PartialEq + 'static, + { + from_iter(seq_to_iter(&seq), None) } - pub fn new_from_kvp_array(a: Array<(K, V)>) -> HashMap { - let it = a.iter().map(|kvp| kvp.clone()); - HashMap(mkRefMut(collections::HashMap::from_iter(it))) + pub fn new_from_enumerable_comparer( + seq: Seq<(K, V)>, + comparer: LrcPtr>, + ) -> HashMap + where + K: Clone + Hash + PartialEq + 'static, + { + from_iter(seq_to_iter(&seq), Some(comparer)) + } + + pub fn new_from_dictionary(map: HashMap) -> HashMap + where + K: Clone + Hash + PartialEq + 'static, + { + from_iter(to_iter(&map), None) + } + + pub fn new_from_dictionary_comparer( + map: HashMap, + comparer: LrcPtr>, + ) -> HashMap + where + K: Clone + Hash + PartialEq + 'static, + { + from_iter(to_iter(&map), Some(comparer)) + } + + pub fn new_from_tuple_array(a: Array>) -> HashMap + where + K: Clone + Hash + PartialEq + 'static, + { + let it = a.iter().map(|tup| tup.as_ref().clone()); + from_iter(it, None) } - pub fn isReadOnly(dict: HashMap) -> bool { + pub fn isReadOnly(map: HashMap) -> bool { false } - pub fn count(dict: HashMap) -> i32 { - dict.len() as i32 + pub fn count(map: HashMap) -> i32 { + map.len() as i32 } - pub fn containsKey(dict: HashMap, k: K) -> bool { - dict.contains_key(&k) + pub fn containsKey(map: HashMap, k: K) -> bool + where + K: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(k, map.comparer.clone()); + map.contains_key(&key) } - pub fn containsValue(dict: HashMap, v: V) -> bool { - dict.values().any(|x| x.eq(&v)) + pub fn containsValue(map: HashMap, v: V) -> bool { + map.values().any(|x| x.eq(&v)) } - pub fn tryAdd(dict: HashMap, k: K, v: V) -> bool { - // dict.get_mut().try_insert(k, v).is_ok() // nightly only - if dict.contains_key(&k) { + pub fn tryAdd(map: HashMap, k: K, v: V) -> bool + where + K: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(k, map.comparer.clone()); + // map.get_mut().try_insert(key, v).is_ok() // nightly only + if map.contains_key(&key) { false } else { - dict.get_mut().insert(k, v).is_none() + map.get_mut().insert(key, v).is_none() } } - pub fn add(dict: HashMap, k: K, v: V) { - match dict.get_mut().insert(k, v) { + pub fn add(map: HashMap, k: K, v: V) + where + K: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(k, map.comparer.clone()); + match map.get_mut().insert(key, v) { Some(v) => { panic!("An item with the same key has already been added.") } @@ -87,16 +191,24 @@ pub mod HashMap_ { } } - pub fn remove(dict: HashMap, k: K) -> bool { - dict.get_mut().remove(&k).is_some() + pub fn remove(map: HashMap, k: K) -> bool + where + K: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(k, map.comparer.clone()); + map.get_mut().remove(&key).is_some() } - pub fn clear(dict: HashMap) { - dict.get_mut().clear(); + pub fn clear(map: HashMap) { + map.get_mut().clear(); } - pub fn get(dict: HashMap, k: K) -> V { - match dict.get_mut().get(&k) { + pub fn get(map: HashMap, k: K) -> V + where + K: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(k, map.comparer.clone()); + match map.get_mut().get(&key) { Some(v) => v.clone(), None => { panic!("The given key was not present in the dictionary.") @@ -104,16 +216,20 @@ pub mod HashMap_ { } } - pub fn set(dict: HashMap, k: K, v: V) { - dict.get_mut().insert(k, v); // ignore return value + pub fn set(map: HashMap, k: K, v: V) + where + K: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(k, map.comparer.clone()); + map.get_mut().insert(key, v); // ignore return value } - pub fn tryGetValue( - dict: HashMap, - k: K, - res: &MutCell, - ) -> bool { - match dict.get_mut().get(&k) { + pub fn tryGetValue(map: HashMap, k: K, res: &MutCell) -> bool + where + K: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(k, map.comparer.clone()); + match map.get_mut().get(&key) { Some(v) => { res.set(v.clone()); true @@ -122,17 +238,15 @@ pub mod HashMap_ { } } - pub fn keys(dict: HashMap) -> Array { - array_from(Vec::from_iter(dict.keys().cloned())) + pub fn keys(map: HashMap) -> Array { + array_from(Vec::from_iter(map.keys().map(|k| k.key.clone()))) } - pub fn values(dict: HashMap) -> Array { - array_from(Vec::from_iter(dict.values().cloned())) + pub fn values(map: HashMap) -> Array { + array_from(Vec::from_iter(map.values().cloned())) } - pub fn entries(dict: HashMap) -> Array<(K, V)> { - array_from(Vec::from_iter( - dict.iter().map(|(k, v)| (k.clone(), v.clone())), - )) + pub fn entries(map: HashMap) -> Array<(K, V)> { + array_from(Vec::from_iter(to_iter(&map))) } } diff --git a/src/fable-library-rust/src/HashSet.rs b/src/fable-library-rust/src/HashSet.rs index 4e45e4ef5f..d35b3f8116 100644 --- a/src/fable-library-rust/src/HashSet.rs +++ b/src/fable-library-rust/src/HashSet.rs @@ -9,42 +9,110 @@ pub mod HashSet_ { #[cfg(not(feature = "no_std"))] use std::collections; - use crate::Native_::{mkRefMut, Lrc, MutCell, Vec}; + use crate::System::Collections::Generic::IEqualityComparer_1; use crate::NativeArray_::{array_from, Array}; - type MutHashSet = MutCell>; + use crate::Native_::{mkRefMut, seq_to_iter, HashKey, Lrc, LrcPtr, MutCell, Seq, Vec}; use core::fmt::{Debug, Display, Formatter, Result}; use core::hash::Hash; - #[repr(transparent)] - #[derive(Clone, Debug, Default)] //, PartialEq, PartialOrd, Eq, Hash, Ord)] - pub struct HashSet(Lrc>); + type MutHashSet = MutCell>>; + + #[derive(Clone)] //, Debug, Default, PartialEq, PartialOrd, Eq, Hash, Ord)] + pub struct HashSet { + hash_set: Lrc>, + comparer: Option>>, + } + + impl Default for HashSet { + fn default() -> HashSet { + new_empty() + } + } impl core::ops::Deref for HashSet { type Target = Lrc>; fn deref(&self) -> &Self::Target { - &self.0 + &self.hash_set + } + } + + impl Debug for HashSet { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{:?}", self.hash_set) //TODO: } } impl Display for HashSet { fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{:?}", self.0) //TODO: + write!(f, "{:?}", self.hash_set) //TODO: + } + } + + fn from_iter>( + iter: I, + comparer: Option>>, + ) -> HashSet + where + T: Clone + Hash + PartialEq + 'static, + { + let it = iter.map(|v| HashKey::new(v, comparer.clone())); + HashSet { + hash_set: mkRefMut(collections::HashSet::from_iter(it)), + comparer: comparer.clone(), } } + fn to_iter(set: &HashSet) -> impl Iterator + '_ { + set.iter().map(|k| k.key.clone()) + } + pub fn new_empty() -> HashSet { - HashSet(mkRefMut(collections::HashSet::new())) + HashSet { + hash_set: mkRefMut(collections::HashSet::new()), + comparer: None, + } } pub fn new_with_capacity(capacity: i32) -> HashSet { - HashSet(mkRefMut(collections::HashSet::with_capacity( - capacity as usize, - ))) + HashSet { + hash_set: mkRefMut(collections::HashSet::with_capacity(capacity as usize)), + comparer: None, + } + } + + pub fn new_with_comparer(comparer: LrcPtr>) -> HashSet { + HashSet { + hash_set: mkRefMut(collections::HashSet::new()), + comparer: Some(comparer), + } + } + + pub fn new_with_capacity_comparer( + capacity: i32, + comparer: LrcPtr>, + ) -> HashSet { + HashSet { + hash_set: mkRefMut(collections::HashSet::with_capacity(capacity as usize)), + comparer: Some(comparer), + } + } + + pub fn new_from_enumerable(seq: Seq) -> HashSet + where + T: Clone + Hash + PartialEq + 'static, + { + from_iter(seq_to_iter(&seq), None) } - pub fn new_from_array(a: Array) -> HashSet { - HashSet(mkRefMut(collections::HashSet::from_iter(a.iter().cloned()))) + pub fn new_from_enumerable_comparer( + seq: Seq, + comparer: LrcPtr>, + ) -> HashSet + where + T: Clone + Hash + PartialEq + 'static, + { + from_iter(seq_to_iter(&seq), Some(comparer)) } pub fn isReadOnly(set: HashSet) -> bool { @@ -55,23 +123,35 @@ pub mod HashSet_ { set.len() as i32 } - pub fn contains(set: HashSet, v: T) -> bool { - set.contains(&v) + pub fn contains(set: HashSet, v: T) -> bool + where + T: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(v, set.comparer.clone()); + set.contains(&key) } - pub fn add(set: HashSet, v: T) -> bool { - set.get_mut().insert(v) + pub fn add(set: HashSet, v: T) -> bool + where + T: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(v, set.comparer.clone()); + set.get_mut().insert(key) } - pub fn remove(set: HashSet, v: T) -> bool { - set.get_mut().remove(&v) + pub fn remove(set: HashSet, v: T) -> bool + where + T: Clone + Hash + PartialEq + 'static, + { + let key = HashKey::new(v, set.comparer.clone()); + set.get_mut().remove(&key) } - pub fn clear(set: HashSet) { + pub fn clear(set: HashSet) { set.get_mut().clear(); } pub fn entries(set: HashSet) -> Array { - array_from(Vec::from_iter(set.iter().cloned())) + array_from(Vec::from_iter(to_iter(&set))) } } diff --git a/src/fable-library-rust/src/Interfaces.fs b/src/fable-library-rust/src/Interfaces.fs index 0ea8fb3f07..6974b5700a 100644 --- a/src/fable-library-rust/src/Interfaces.fs +++ b/src/fable-library-rust/src/Interfaces.fs @@ -1,84 +1,81 @@ -module Interfaces_ +namespace System -module System = +type IDisposable = + abstract Dispose: unit -> unit - type IDisposable = - abstract Dispose: unit -> unit +type IEquatable<'T> = + abstract Equals: 'T -> bool - type IEquatable<'T> = - abstract Equals: 'T -> bool +type IComparable = + abstract CompareTo: obj -> int - type IComparable = - abstract CompareTo: obj -> int +type IComparable<'T> = + abstract CompareTo: 'T -> int - type IComparable<'T> = - abstract CompareTo: 'T -> int +type IFormatProvider = + abstract GetFormat: System.Type -> obj - type IFormatProvider = - abstract GetFormat: System.Type -> obj +type IFormattable = + abstract ToString: string * IFormatProvider -> string - type IFormattable = - abstract ToString: string * IFormatProvider -> string - module Collections = +namespace System.Collections - type IComparer = - abstract Compare: obj * obj -> int +type IComparer = + abstract Compare: obj * obj -> int - type IEqualityComparer = - abstract Equals: obj * obj -> bool - abstract GetHashCode: obj -> int +type IEqualityComparer = + abstract Equals: obj * obj -> bool + abstract GetHashCode: obj -> int - type IEnumerator = - inherit System.IDisposable - abstract Current: obj - abstract MoveNext: unit -> bool - abstract Reset: unit -> unit +type IEnumerator = + abstract Current: obj + abstract MoveNext: unit -> bool + abstract Reset: unit -> unit - type IEnumerable = - abstract GetEnumerator: unit -> IEnumerator +type IEnumerable = + abstract GetEnumerator: unit -> IEnumerator - type IStructuralComparable = - abstract CompareTo: obj * IComparer -> int +type IStructuralComparable = + abstract CompareTo: obj * IComparer -> int - type IStructuralEquatable = - abstract Equals: obj * IEqualityComparer -> bool - abstract GetHashCode: IEqualityComparer -> int +type IStructuralEquatable = + abstract Equals: obj * IEqualityComparer -> bool + abstract GetHashCode: IEqualityComparer -> int - module Generic = - type IComparer<'T> = - abstract Compare: 'T * 'T -> int +namespace System.Collections.Generic - type IEqualityComparer<'T> = - abstract Equals: 'T * 'T -> bool - abstract GetHashCode: 'T -> int +type IComparer<'T> = + abstract Compare: 'T * 'T -> int - type IEnumerator<'T> = - inherit System.IDisposable - // inherit System.Collections.IEnumerator - abstract Current: 'T - abstract MoveNext: unit -> bool - abstract Reset: unit -> unit +type IEqualityComparer<'T> = + abstract Equals: 'T * 'T -> bool + abstract GetHashCode: 'T -> int - type IEnumerable<'T> = - // inherit System.Collections.IEnumerable - abstract GetEnumerator: unit -> IEnumerator<'T> +type IEnumerator<'T> = + inherit System.IDisposable + inherit System.Collections.IEnumerator + abstract Current: 'T - type ICollection<'T> = - abstract Count: int - abstract IsReadOnly: bool - abstract Contains: 'T -> bool - abstract Add: 'T -> unit - abstract Remove: 'T -> bool - abstract Clear: unit -> unit - abstract CopyTo: 'T[] * int -> unit +type IEnumerable<'T> = + inherit System.Collections.IEnumerable + abstract GetEnumerator: unit -> IEnumerator<'T> - type IDictionary<'K, 'V> = - abstract Item: 'K -> 'V with get //, set //TODO: support property setters - abstract Keys: ICollection<'K> - abstract Values: ICollection<'V> - abstract Add: 'K * 'V -> unit - abstract ContainsKey: 'K -> bool - abstract TryGetValue: 'K * byref<'V> -> bool - abstract Remove: 'K -> bool +type ICollection<'T> = + abstract Count: int + abstract IsReadOnly: bool + abstract Contains: 'T -> bool + abstract Add: 'T -> unit + abstract Remove: 'T -> bool + abstract Clear: unit -> unit + abstract CopyTo: 'T[] * int -> unit + +type IDictionary<'K, 'V> = + abstract Item: 'K -> 'V with get, set + abstract Keys: ICollection<'K> + abstract Values: ICollection<'V> + abstract Add: 'K * 'V -> unit + abstract ContainsKey: 'K -> bool + abstract TryGetValue: 'K * byref<'V> -> bool + abstract Remove: 'K -> bool diff --git a/src/fable-library-rust/src/Native.rs b/src/fable-library-rust/src/Native.rs index 25bb2278a7..472fdf20eb 100644 --- a/src/fable-library-rust/src/Native.rs +++ b/src/fable-library-rust/src/Native.rs @@ -55,12 +55,16 @@ pub mod Native_ { value } + use crate::System::Collections::Generic::IEnumerable_1; + use crate::System::Collections::Generic::IEqualityComparer_1; + // TODO: use these types in generated code - pub type Seq = LrcPtr>; + pub type Seq = LrcPtr>; pub type RefCell = LrcPtr>; pub type Nullable = Option>; use core::cmp::Ordering; + use core::fmt::{Debug, Display, Formatter, Result}; use core::hash::{BuildHasher, Hash, Hasher}; // ----------------------------------------------------------- @@ -136,6 +140,56 @@ pub mod Native_ { } } + // ----------------------------------------------------------- + // IEqualityComparer key wrapper + // ----------------------------------------------------------- + + #[derive(Clone)] + pub struct HashKey { + pub key: T, + pub comparer: Option>>, + } + + impl HashKey { + pub fn new(key: T, comparer: Option>>) -> HashKey { + HashKey { key, comparer } + } + } + + impl Debug for HashKey { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_tuple("ComparerKey").field(&self.key).finish() + } + } + + impl Display for HashKey { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + self.key.fmt(f) + } + } + + impl Hash for HashKey { + #[inline] + fn hash(&self, state: &mut H) { + match &self.comparer { + Some(comp) => comp.GetHashCode(self.key.clone()).hash(state), + None => self.key.hash(state), + } + } + } + + impl PartialEq for HashKey { + #[inline] + fn eq(&self, other: &Self) -> bool { + match &self.comparer { + Some(comp) => comp.Equals(self.key.clone(), other.key.clone()), + None => self.key.eq(&other.key), + } + } + } + + impl Eq for HashKey {} + // ----------------------------------------------------------- // Type testing // ----------------------------------------------------------- diff --git a/src/fable-library-rust/src/RegExp.rs b/src/fable-library-rust/src/RegExp.rs index dd2a7f10f7..3b234836cc 100644 --- a/src/fable-library-rust/src/RegExp.rs +++ b/src/fable-library-rust/src/RegExp.rs @@ -3,7 +3,7 @@ pub mod RegExp_ { use core::borrow::{Borrow, BorrowMut}; use crate::{ - Interfaces_::System::Collections::Generic::{IEnumerable_1, IEnumerator_1}, + System::Collections::Generic::{IEnumerable_1, IEnumerator_1}, NativeArray_::{array_from, new_array, new_empty, Array}, Native_::{Func1, LrcPtr, ToString, Vec}, Seq_::ofArray, diff --git a/src/fable-library-rust/src/Seq.fs b/src/fable-library-rust/src/Seq.fs index f65ef160c5..205d7b3e2e 100644 --- a/src/fable-library-rust/src/Seq.fs +++ b/src/fable-library-rust/src/Seq.fs @@ -6,13 +6,7 @@ module Seq_ open Global_ -open Interfaces_ -//open System.Collections.Generic - -type IEnumerable<'T> = System.Collections.Generic.IEnumerable<'T> -type IEnumerator<'T> = System.Collections.Generic.IEnumerator<'T> - -type 'T seq = IEnumerable<'T> +open System.Collections.Generic let inline indexNotFound () = failwith SR.keyNotFoundAlt @@ -27,8 +21,10 @@ module Enumerable = type Enumerable<'T>(f) = interface IEnumerable<'T> with member _.GetEnumerator() = f () - // interface System.Collections.IEnumerable with - // member _.GetEnumerator() = f() :> System.Collections.IEnumerator + + interface System.Collections.IEnumerable with + member _.GetEnumerator() = f () :> System.Collections.IEnumerator + // override xs.ToString() = // let maxCount = 4 // let mutable i = 0 @@ -49,22 +45,17 @@ module Enumerable = interface IEnumerator<'T> with member _.Current = curr.Value + interface System.Collections.IEnumerator with + member _.Current = curr.Value + member _.MoveNext() = curr <- next () curr.IsSome member _.Reset() = () + + interface System.IDisposable with member _.Dispose() = dispose () - // interface System.Collections.IEnumerator with - // member _.Current = - // curr.Value - // member _.MoveNext() = - // curr <- next() - // curr.IsSome - // member _.Reset() = () - // member _.Dispose() = dispose() - // interface System.IDisposable with - // member _.Dispose() = dispose() let fromFunction next : IEnumerator<'T> = let dispose () = () diff --git a/src/fable-library-ts/Fable.Library.TypeScript.fsproj b/src/fable-library-ts/Fable.Library.TypeScript.fsproj index f8047462ee..02beb89615 100644 --- a/src/fable-library-ts/Fable.Library.TypeScript.fsproj +++ b/src/fable-library-ts/Fable.Library.TypeScript.fsproj @@ -3,7 +3,7 @@ net8.0 $(DefineConstants);FABLE_COMPILER - $(DefineConstants);FX_NO_BIGINT + diff --git a/tests/Dart/src/ComparisonTests.fs b/tests/Dart/src/ComparisonTests.fs index e62aa920e8..066ef2c2e2 100644 --- a/tests/Dart/src/ComparisonTests.fs +++ b/tests/Dart/src/ComparisonTests.fs @@ -360,7 +360,7 @@ let tests() = testCase "max works with primitives" <| fun () -> max 1 2 |> equal 2 - // max 1m 2m |> equal 2m // TODO: + // max 10m 2m |> equal 10m // TODO: Math.Max(1, 2) |> equal 2 max "a" "b" |> equal "b" @@ -376,7 +376,7 @@ let tests() = testCase "min works with primitives" <| fun () -> min 1 2 |> equal 1 - // min 1m 2m |> equal 1m // TODO: + // min 10m 2m |> equal 2m // TODO: Math.Min(1, 2) |> equal 1 min "a" "b" |> equal "a" diff --git a/tests/Js/Main/ComparisonTests.fs b/tests/Js/Main/ComparisonTests.fs index fdb3c4c723..5ec5f82f5a 100644 --- a/tests/Js/Main/ComparisonTests.fs +++ b/tests/Js/Main/ComparisonTests.fs @@ -373,7 +373,7 @@ let tests = testCase "max works with primitives" <| fun () -> max 1 2 |> equal 2 - max 1m 2m |> equal 2m + max 10m 2m |> equal 10m Math.Max(1, 2) |> equal 2 max "a" "b" |> equal "b" @@ -389,7 +389,7 @@ let tests = testCase "min works with primitives" <| fun () -> min 1 2 |> equal 1 - min 1m 2m |> equal 1m + min 10m 2m |> equal 2m Math.Min(1, 2) |> equal 1 min "a" "b" |> equal "a" diff --git a/tests/Python/TestComparison.fs b/tests/Python/TestComparison.fs index 81c9483d09..9ba560e3d4 100644 --- a/tests/Python/TestComparison.fs +++ b/tests/Python/TestComparison.fs @@ -440,7 +440,7 @@ let ``test Comparison with objects implementing IComparable works`` () = [] let ``test max works with primitives`` () = max 1 2 |> equal 2 - max 1m 2m |> equal 2m + max 10m 2m |> equal 10m Math.Max(1, 2) |> equal 2 max "a" "b" |> equal "b" @@ -459,7 +459,7 @@ let ``test max with objects implementing IComparable works`` () = [] let ``test min works with primitives`` () = min 1 2 |> equal 1 - min 1m 2m |> equal 1m + min 10m 2m |> equal 2m System.Math.Min(1, 2) |> equal 1 min "a" "b" |> equal "a" diff --git a/tests/Rust/tests/src/ComparisonTests.fs b/tests/Rust/tests/src/ComparisonTests.fs index 488fe93039..a450935eff 100644 --- a/tests/Rust/tests/src/ComparisonTests.fs +++ b/tests/Rust/tests/src/ComparisonTests.fs @@ -387,7 +387,7 @@ let ``Record comparison works`` () = [] let ``max works with primitives`` () = max 1 2 |> equal 2 - max 1m 2m |> equal 2m + max 10m 2m |> equal 10m System.Math.Max(1, 2) |> equal 2 max "a" "b" |> equal "b" @@ -406,7 +406,7 @@ let ``max works with records`` () = [] let ``min works with primitives`` () = min 1 2 |> equal 1 - min 1m 2m |> equal 1m + min 10m 2m |> equal 2m System.Math.Min(1, 2) |> equal 1 min "a" "b" |> equal "a" diff --git a/tests/Rust/tests/src/DictionaryTests.fs b/tests/Rust/tests/src/DictionaryTests.fs index f60885aa8e..dc6a50b735 100644 --- a/tests/Rust/tests/src/DictionaryTests.fs +++ b/tests/Rust/tests/src/DictionaryTests.fs @@ -41,13 +41,13 @@ let ``IDictionary ctor works`` () = let dict = dict <| seq { ("A", 1); ("B", 2); ("A", 3) } dict.Count |> equal 2 -// [] -// let ``Dictionary ctor from IDictionary works`` () = -// let idic = dict <| seq { ("A", 1); ("B", 2); ("A", 3) } -// let dict = Dictionary<_,_>(idic) -// dict.Add("100", 100) -// equal 3 idic.Count -// equal 4 dict.Count +[] +let ``Dictionary ctor from IDictionary works`` () = + let idic = dict <| seq { ("A", 1); ("B", 2); ("A", 3) } + let dict = Dictionary<_,_>(idic) + dict.Add("100", 100) + idic.Count |> equal 2 + dict.Count |> equal 3 // [] // let ``Dictionaries with IEqualityComparer work`` () = @@ -68,6 +68,26 @@ let ``IDictionary ctor works`` () = // dic2.ContainsKey(y) |> equal true // dic2.ContainsKey(z) |> equal false +[] +let ``Dictionaries with IEqualityComparer work II`` () = + let x = { a = 4 } + let y = { a = 4 } + let z = { a = 6 } + let dic1 = Dictionary<_,_>() + dic1.Add(x, "foo") + dic1.ContainsKey(x) |> equal true + dic1.ContainsKey(y) |> equal true + dic1.ContainsKey(z) |> equal false + let comparer = + { new IEqualityComparer with + member _.Equals(x, y) = x.a = y.a + member _.GetHashCode(x) = x.a } + let dic2 = Dictionary<_,_>(comparer) + dic2.Add(x, "bar") + dic2.ContainsKey(x) |> equal true + dic2.ContainsKey(y) |> equal true + dic2.ContainsKey(z) |> equal false + [] let ``Interface IDictionary iteration works`` () = let dict = dict <| seq { ("A", 1); ("B", 2); ("A", 3) } diff --git a/tests/Rust/tests/src/HashSetTests.fs b/tests/Rust/tests/src/HashSetTests.fs index 79d84965d0..38aa62cecd 100644 --- a/tests/Rust/tests/src/HashSetTests.fs +++ b/tests/Rust/tests/src/HashSetTests.fs @@ -13,12 +13,12 @@ let inline private hashset xs = type MyRecord = { a: int } -// type R = { i: int; s: string } +type R = { i: int; s: string } -// type Apa<'t when 't : equality>() = -// let state = HashSet<'t>() -// member _.Add t = state.Add t |> ignore -// member _.Contains t = state.Contains t +type Apa<'t when 't : equality>() = + let state = HashSet<'t>() + member _.Add t = state.Add t |> ignore + member _.Contains t = state.Contains t [] let ``HashSet ctor works`` () = @@ -253,9 +253,9 @@ let ``HashSet.Remove with records works`` () = xs.Remove x1 |> equal true xs.Count |> equal 0 -// [] -// let ``HashSet equality works with generics`` () = // See #1712 -// let apa = Apa() -// apa.Add({ i = 5; s = "foo"}) -// apa.Contains ({ i = 5; s = "foo"}) |> equal true -// apa.Contains ({ i = 5; s = "fo"}) |> equal false +[] +let ``HashSet equality works with generics`` () = // See #1712 + let apa = Apa() + apa.Add({ i = 5; s = "foo"}) + apa.Contains ({ i = 5; s = "foo"}) |> equal true + apa.Contains ({ i = 5; s = "fo"}) |> equal false diff --git a/tests/Rust/tests/src/InterfaceTests.fs b/tests/Rust/tests/src/InterfaceTests.fs index 916e038b94..9bbc2e4beb 100644 --- a/tests/Rust/tests/src/InterfaceTests.fs +++ b/tests/Rust/tests/src/InterfaceTests.fs @@ -18,14 +18,14 @@ type Adder3 (m: int) = let addWithAdder (i: IHasAdd) = i.Add 3 4 -// let adderObj = Adder() +let adderObj = Adder() // let adderInt = Adder() :> IHasAdd -// [] -// let ``Module let object value works`` () = -// let a = adderObj :> IHasAdd -// let res = a.Add 2 1 -// res |> equal 3 +[] +let ``Module let object value works`` () = + let a = adderObj :> IHasAdd + let res = a.Add 2 1 + res |> equal 3 // [] // let ``Module let interface value works`` () =