From 29365f7ca5ac78d61749fec229b135ff278e8193 Mon Sep 17 00:00:00 2001 From: ncave <777696+ncave@users.noreply.github.com> Date: Tue, 14 Nov 2023 16:16:42 -0800 Subject: [PATCH] Fixes #3584 --- .vscode/settings.json | 2 + src/Fable.Cli/CHANGELOG.md | 4 + src/Fable.Transforms/Dart/Fable2Dart.fs | 30 +- src/Fable.Transforms/FSharp2Fable.Util.fs | 28 +- src/Fable.Transforms/Fable2Babel.fs | 29 +- src/Fable.Transforms/Python/Fable2Python.fs | 28 +- src/Fable.Transforms/Rust/Fable2Rust.fs | 32 +- tests/Dart/src/ArrayTests.fs | 2 +- tests/Js/Main/ArrayTests.fs | 2 +- tests/Js/Main/MiscTests.fs | 8 + tests/Python/TestMisc.fs | 14 +- tests/Rust/tests/src/ApplicativeTests.fs | 98 +- tests/Rust/tests/src/MiscTests.fs | 1435 +++++++++++++++++-- tests/Rust/tests/src/MiscTests2.fs | 1428 ++---------------- 14 files changed, 1584 insertions(+), 1556 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index bc38a09f63..a6b42ac911 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,6 +23,8 @@ "src/fcs-fable" ], "rust-analyzer.linkedProjects": [ + "build/fable-library-rust/Cargo.toml", + "build/tests/Rust/Cargo.toml", "temp/fable-library-rust/Cargo.toml", "temp/tests/Rust/Cargo.toml" ], diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index d49b25c50b..46b8fddb85 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +#### All + +* Fix #3584: Unit type compiles to undeclared variable (by @ncave) + #### Rust * Added `Guid.TryParse`, `Guid.ToByteArray` (by @ncave) diff --git a/src/Fable.Transforms/Dart/Fable2Dart.fs b/src/Fable.Transforms/Dart/Fable2Dart.fs index b398fb46f3..a90ab6ea1e 100644 --- a/src/Fable.Transforms/Dart/Fable2Dart.fs +++ b/src/Fable.Transforms/Dart/Fable2Dart.fs @@ -110,13 +110,6 @@ module Util = let fn = com.GetImportIdent(ctx, memberName, modulePath, Fable.Any) Expression.invocationExpression(fn.Expr, args, transformType com ctx t) - let discardUnitArg (args: Fable.Ident list) = - match args with - | [] -> [] - | [unitArg] when unitArg.Type = Fable.Unit -> [] - | [thisArg; unitArg] when thisArg.IsThisArgument && unitArg.Type = Fable.Unit -> [thisArg] - | args -> args - let addErrorAndReturnNull (com: Compiler) (range: SourceLocation option) (error: string) = addError com [] range error NullLiteral Dynamic |> Literal @@ -244,8 +237,11 @@ module Util = type NamedTailCallOpportunity(_com: IDartCompiler, ctx, name, args: Fable.Ident list) = // Capture the current argument values to prevent delayed references from getting corrupted, // for that we use block-scoped ES2015 variable declarations. See #681, #1859 - let argIds = discardUnitArg args |> List.map (fun arg -> - getUniqueNameInDeclarationScope ctx (arg.Name + "_mut")) + let argIds = + args + |> FSharp2Fable.Util.discardUnitArg + |> List.map (fun arg -> + getUniqueNameInDeclarationScope ctx (arg.Name + "_mut")) interface ITailCallOpportunity with member _.Label = name member _.Args = argIds @@ -359,12 +355,16 @@ module Util = statements1 @ statements2 @ [Statement.continueStatement(tc.Label)] let transformCallArgs (com: IDartCompiler) ctx (info: ArgsInfo) = + let paramsInfo, thisArg, args = match info with - | NoCallInfo args -> None, None, args + | NoCallInfo args -> + let args = FSharp2Fable.Util.dropUnitCallArg args [] + None, None, args | CallInfo callInfo -> + let args = FSharp2Fable.Util.dropUnitCallArg callInfo.Args callInfo.SignatureArgTypes let paramsInfo = callInfo.MemberRef |> Option.bind com.TryGetMember |> Option.map getParamsInfo - paramsInfo, callInfo.ThisArg, callInfo.Args + paramsInfo, callInfo.ThisArg, args let unnamedArgs, namedArgs = paramsInfo @@ -383,7 +383,6 @@ module Util = let unnamedArgs = match unnamedArgs, paramsInfo with - | [Fable.Value(Fable.UnitConstant,_)], _ -> [] | args, Some paramsInfo -> let argsLen = args.Length let parameters = paramsInfo.Parameters @@ -833,7 +832,10 @@ module Util = let invocation = match args with | [] -> Expression.invocationExpression(callee, t) - | args -> (callee, args) ||> List.fold (fun e arg -> Expression.invocationExpression(e, [arg], t)) + | args -> + (callee, args) + ||> List.fold (fun expr arg -> + Expression.invocationExpression(expr, [arg], t)) let statements2, capturedExpr = resolveExpr returnStrategy invocation statements @ statements2, capturedExpr @@ -1062,7 +1064,7 @@ module Util = let tailcallChance = Option.map (fun name -> NamedTailCallOpportunity(com, ctx, name, args) :> ITailCallOpportunity) name - let args = discardUnitArg args + let args = FSharp2Fable.Util.discardUnitArg args let mutable isTailCallOptimized = false let varsInScope = args |> List.map (fun a -> a.Name) |> HashSet let ctx = diff --git a/src/Fable.Transforms/FSharp2Fable.Util.fs b/src/Fable.Transforms/FSharp2Fable.Util.fs index f5a13e3d5f..012e707ab8 100644 --- a/src/Fable.Transforms/FSharp2Fable.Util.fs +++ b/src/Fable.Transforms/FSharp2Fable.Util.fs @@ -1405,6 +1405,26 @@ module Util = open TypeHelpers open Identifiers + let isUnitArg (ident: Fable.Ident) = + ident.IsCompilerGenerated + && ident.Type = Fable.Unit + // && (ident.DisplayName.StartsWith("unitVar") || ident.DisplayName.Contains("@")) + + let discardUnitArg (args: Fable.Ident list) = + match args with + | [] -> [] + | [arg] when isUnitArg arg -> [] + | [thisArg; arg] when thisArg.IsThisArgument && isUnitArg arg -> [thisArg] + | args -> args + + let dropUnitCallArg (args: Fable.Expr list) (argTypes: Fable.Type list) = + match args, argTypes with + // Don't remove unit arg if a generic is expected + | [MaybeCasted(Fable.Value(Fable.UnitConstant,_))], [Fable.GenericParam _] -> args + | [MaybeCasted(Fable.Value(Fable.UnitConstant,_))], _ -> [] + | [Fable.IdentExpr ident], _ when isUnitArg ident -> [] + | _ -> args + let makeFunctionArgs com ctx (args: FSharpMemberOrFunctionOrValue list) = let ctx, args = ((ctx, []), args) @@ -2168,10 +2188,10 @@ module Util = let makeValueFrom (com: IFableCompiler) (ctx: Context) r (v: FSharpMemberOrFunctionOrValue) = let typ = makeType ctx.GenericArgs v.FullType match v, v.DeclaringEntity with - | _ when typ = Fable.Unit -> - if com.Options.Verbosity = Verbosity.Verbose && not v.IsCompilerGenerated then // See #1516 - $"Value %s{v.DisplayName} is replaced with unit constant" - |> addWarning com ctx.InlinePath r + | _ when typ = Fable.Unit && v.IsCompilerGenerated -> + // if com.Options.Verbosity = Verbosity.Verbose && not v.IsCompilerGenerated then // See #1516 + // $"Value %s{v.DisplayName} is replaced with unit constant" + // |> addWarning com ctx.InlinePath r Fable.Value(Fable.UnitConstant, r) | Emitted com r typ None emitted, _ -> emitted | Imported com r typ None imported -> imported diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs index 08b0712785..532078575f 100644 --- a/src/Fable.Transforms/Fable2Babel.fs +++ b/src/Fable.Transforms/Fable2Babel.fs @@ -849,13 +849,6 @@ module Util = | Fable.LetRec(bindings, body) -> Some(bindings, body) | _ -> None - let discardUnitArg (args: Fable.Ident list) = - match args with - | [] -> [] - | [unitArg] when unitArg.Type = Fable.Unit -> [] - | [thisArg; unitArg] when thisArg.IsThisArgument && unitArg.Type = Fable.Unit -> [thisArg] - | args -> args - let getUniqueNameInRootScope (ctx: Context) name = let name = (name, Naming.NoMemberPart) ||> Naming.sanitizeIdent (fun name -> ctx.UsedNames.RootScope.Contains(name) @@ -872,8 +865,11 @@ module Util = type NamedTailCallOpportunity(_com: Compiler, ctx, name, args: Fable.Ident list) = // Capture the current argument values to prevent delayed references from getting corrupted, // for that we use block-scoped ES2015 variable declarations. See #681, #1859 - let argIds = args |> discardUnitArg |> List.map (fun arg -> - getUniqueNameInDeclarationScope ctx (arg.Name + "_mut")) + let argIds = + args + |> FSharp2Fable.Util.discardUnitArg + |> List.map (fun arg -> + getUniqueNameInDeclarationScope ctx (arg.Name + "_mut")) interface ITailCallOpportunity with member _.Label = name member _.Args = argIds @@ -1516,13 +1512,8 @@ module Util = Expression.newExpression(classExpr, [||]) let transformCallArgs (com: IBabelCompiler) ctx (callInfo: Fable.CallInfo) (memberInfo: Fable.MemberFunctionOrValue option) = - let args = - match callInfo.Args, callInfo.SignatureArgTypes with - // Don't remove unit arg if a generic is expected, TypeScript will complain - | [Fable.Value(Fable.UnitConstant,_)], [Fable.GenericParam _] -> callInfo.Args - | [Fable.Value(Fable.UnitConstant,_)], _ -> [] - | _ -> callInfo.Args + let args = FSharp2Fable.Util.dropUnitCallArg callInfo.Args callInfo.SignatureArgTypes let paramsInfo = Option.map getParamsInfo memberInfo let args = @@ -1746,14 +1737,14 @@ module Util = let transformCurriedApply com ctx range (TransformExpr com ctx applied) args = (applied, args) - ||> List.fold (fun e arg -> + ||> List.fold (fun expr arg -> match arg with // TODO: If arg type is unit but it's an expression with potential // side-effects, we need to extract it and execute it before the call | Fable.Value(Fable.UnitConstant,_) -> [] | Fable.IdentExpr ident when ident.Type = Fable.Unit -> [] | TransformExpr com ctx arg -> [arg] - |> callFunction com ctx range e []) + |> callFunction com ctx range expr []) let transformCallAsStatements com ctx range t returnStrategy callee callInfo = let argsLen (i: Fable.CallInfo) = @@ -2402,7 +2393,7 @@ module Util = let tailcallChance = Option.map (fun name -> NamedTailCallOpportunity(com, ctx, name, args) :> ITailCallOpportunity) name - let args = discardUnitArg args + let args = FSharp2Fable.Util.discardUnitArg args let declaredVars = ResizeArray() let mutable isTailCallOptimized = false let ctx = @@ -2883,7 +2874,7 @@ module Util = let args = info.CurriedParameterGroups |> List.concat - // |> discardUnitArg + // |> FSharp2Fable.Util.discardUnitArg |> List.toArray let argsLen = Array.length args diff --git a/src/Fable.Transforms/Python/Fable2Python.fs b/src/Fable.Transforms/Python/Fable2Python.fs index e93dc9c582..835199bc2c 100644 --- a/src/Fable.Transforms/Python/Fable2Python.fs +++ b/src/Fable.Transforms/Python/Fable2Python.fs @@ -997,17 +997,6 @@ module Util = | Fable.Delegate (args, body, _, []) -> Some(args, body) | _ -> None - let discardUnitArg (args: Fable.Ident list) = - match args with - | [] -> [] - | [ unitArg ] when unitArg.Type = Fable.Unit -> [] - | [ thisArg; unitArg ] when - thisArg.IsThisArgument - && unitArg.Type = Fable.Unit - -> - [ thisArg ] - | args -> args - let getUniqueNameInRootScope (ctx: Context) name = let name = (name, Naming.NoMemberPart) @@ -1036,7 +1025,8 @@ module Util = // for that we use block-scoped ES2015 variable declarations. See #681, #1859 // TODO: Local unique ident names let argIds = - discardUnitArg args + args + |> FSharp2Fable.Util.discardUnitArg |> List.map (fun arg -> let name = getUniqueNameInDeclarationScope ctx (arg.Name + "_mut") // Ignore type annotation here as it generates unnecessary typevars @@ -1856,8 +1846,9 @@ module Util = Expression.call (Expression.name name), [ stmt ] @ stmts let transformCallArgs (com: IPythonCompiler) ctx (callInfo: Fable.CallInfo) : Expression list * Keyword list * Statement list = + + let args = FSharp2Fable.Util.dropUnitCallArg callInfo.Args callInfo.SignatureArgTypes let paramsInfo = callInfo.MemberRef |> Option.bind com.TryGetMember |> Option.map getParamsInfo - let args = callInfo.Args let args, objArg, stmts = paramsInfo @@ -1890,8 +1881,7 @@ module Util = let args, stmts' = match args with - | [] - | [ MaybeCasted (Fable.Value (Fable.UnitConstant, _)) ] -> [], [] + | [] -> [], [] | args when hasSpread -> match List.rev args with | [] -> [], [] @@ -2065,8 +2055,10 @@ module Util = match arg with // TODO: If arg type is unit but it's an expression with potential // side-effects, we need to extract it and execute it before the call - | Fable.Value(Fable.UnitConstant,_) -> [], [] - | Fable.IdentExpr ident when ident.Type = Fable.Unit -> [], [] + + // TODO: discardUnitArg may still be needed in some cases + // | Fable.Value(Fable.UnitConstant,_) -> [], [] + // | Fable.IdentExpr ident when ident.Type = Fable.Unit -> [], [] | TransformExpr com ctx (arg, stmts') -> [arg], stmts' callFunction range applied args [], stmts @ stmts') @@ -3232,7 +3224,7 @@ module Util = let tailcallChance = Option.map (fun name -> NamedTailCallOpportunity(com, ctx, name, args) :> ITailCallOpportunity) name - let args = discardUnitArg args + let args = FSharp2Fable.Util.discardUnitArg args /// Removes `_mut` or `_mut_1` suffix from the identifier name let cleanName (input: string) = Regex.Replace(input, @"_mut(_\d+)?$", "") diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs index 2a0551c215..2185b170df 100644 --- a/src/Fable.Transforms/Rust/Fable2Rust.fs +++ b/src/Fable.Transforms/Rust/Fable2Rust.fs @@ -1158,24 +1158,19 @@ module Util = | _ -> None let isUnitArg (ident: Fable.Ident) = - ident.IsCompilerGenerated && - ident.Type = Fable.Unit && - (ident.DisplayName.StartsWith("unitVar") || ident.DisplayName.Contains("@")) + ident.IsCompilerGenerated + && ident.Type = Fable.Unit + && (ident.DisplayName.StartsWith("unitVar") || ident.DisplayName.Contains("@")) let discardUnitArg (genArgs: Fable.Type list) (args: Fable.Ident list) = match genArgs, args with | [Fable.Unit], [arg] -> args // don't drop unit arg when generic arg is unit - | _, [] -> [] - | _, [arg] when isUnitArg arg -> [] - | _, [thisArg; arg] when thisArg.IsThisArgument && isUnitArg arg -> [thisArg] - | _, args -> args - - let dropUnitCallArg (genArgs: Fable.Type list) (args: Fable.Expr list) = - match genArgs, args with - | [Fable.Unit], [arg] -> args // don't drop unit arg when generic arg is unit - | _, [MaybeCasted(Fable.Value(Fable.UnitConstant, _))] -> [] - | _, [Fable.IdentExpr ident] when isUnitArg ident -> [] - | _, args -> args + | _ -> + match args with + | [] -> [] + | [arg] when isUnitArg arg -> [] + | [thisArg; arg] when thisArg.IsThisArgument && isUnitArg arg -> [thisArg] + | args -> args /// Fable doesn't currently sanitize attached members/fields so we do a simple sanitation here. /// Should this be done in FSharp2Fable step? @@ -2151,7 +2146,7 @@ module Util = { ctx with RequiresSendSync = isSendSync IsParamByRefPreferred = isByRefPreferred } - let args = dropUnitCallArg callInfo.GenericArgs callInfo.Args + let args = FSharp2Fable.Util.dropUnitCallArg callInfo.Args callInfo.SignatureArgTypes let args = transformCallArgs com ctx args callInfo.SignatureArgTypes argParams match calleeExpr with @@ -2622,9 +2617,10 @@ module Util = optimizeTailCall com ctx r tc args | _ -> let callee = transformCallee com ctx calleeExpr - (callee, args) ||> List.fold (fun c arg -> - let args = dropUnitCallArg [] [arg] - callFunction com ctx r c args) + (callee, args) + ||> List.fold (fun expr arg -> + let args = FSharp2Fable.Util.dropUnitCallArg [arg] [] + callFunction com ctx r expr args) let makeUnionCasePat unionCaseName fields = if List.isEmpty fields then diff --git a/tests/Dart/src/ArrayTests.fs b/tests/Dart/src/ArrayTests.fs index 53f213bb2f..e58f77ebb2 100644 --- a/tests/Dart/src/ArrayTests.fs +++ b/tests/Dart/src/ArrayTests.fs @@ -285,7 +285,7 @@ let tests () = ys.[0] + ys.[1] |> equal 3. - testCase "Array.concat works with strings" <| fun test -> + testCase "Array.concat works with strings" <| fun () -> [| [| "One" |]; [| "Two" |] |] |> Array.concat |> List.ofArray diff --git a/tests/Js/Main/ArrayTests.fs b/tests/Js/Main/ArrayTests.fs index f0b2adb1d3..ad5552b7f7 100644 --- a/tests/Js/Main/ArrayTests.fs +++ b/tests/Js/Main/ArrayTests.fs @@ -361,7 +361,7 @@ let tests = ys.[0] + ys.[1] |> equal 3. - testCase "Array.concat works with strings" <| fun test -> + testCase "Array.concat works with strings" <| fun () -> [| [| "One" |]; [| "Two" |] |] |> Array.concat |> List.ofArray diff --git a/tests/Js/Main/MiscTests.fs b/tests/Js/Main/MiscTests.fs index 858fde9dcf..cd3774ac8e 100644 --- a/tests/Js/Main/MiscTests.fs +++ b/tests/Js/Main/MiscTests.fs @@ -477,9 +477,17 @@ let inline inlineLambdaWithAnonRecord callback = let sideEffect() = () +let inline inlineToString (f: 'T -> string): 'T -> string = + let unused = f + fun a -> $"{a}" + let tests = testList "Miscellaneous" [ + testCase "Generic unit args work" <| fun _ -> // #3584 + let to_str = inlineToString (fun (props: unit) -> "s") + to_str () |> equal $"{()}" + #if FABLE_COMPILER #if !FABLE_COMPILER_JAVASCRIPT testCase "Fable.JsonProvider works" <| fun _ -> diff --git a/tests/Python/TestMisc.fs b/tests/Python/TestMisc.fs index fb34c01bc1..b6f3201ea5 100644 --- a/tests/Python/TestMisc.fs +++ b/tests/Python/TestMisc.fs @@ -454,6 +454,10 @@ let inline inlineLambdaWithAnonRecord callback = let sideEffect() = () +let inline inlineToString (f: 'T -> string): 'T -> string = + let unused = f + fun a -> $"{a}" + type Union_TestUnionTag = Union_TestUnionTag of int [] @@ -462,14 +466,18 @@ type FooWithAttachedMembers () = static member Foo = FooWithAttachedMembers() - -#if FABLE_COMPILER +[] +let ``test Generic unit args work`` () = // #3584 + let to_str = inlineToString (fun (props: unit) -> "s") + to_str () |> equal $"{()}" [] let ``test lambdas returning member expression accessing JS object work`` () = // #2311 let x = inlineLambdaWithAnonRecord (fun x -> x.A) x() |> equal 1 +#if FABLE_COMPILER + [] let ``test can check compiler version with constant`` () = let mutable x = 0 @@ -1366,9 +1374,7 @@ let ``test Module mutable option values work`` () = Util.mutableValueOpt <- None Util.mutableValueOpt.IsNone |> equal true - [] let ``test attached static getters works`` () = - let result = FooWithAttachedMembers.Foo.Bar result |> equal 42 diff --git a/tests/Rust/tests/src/ApplicativeTests.fs b/tests/Rust/tests/src/ApplicativeTests.fs index d217897110..6b6dc538f7 100644 --- a/tests/Rust/tests/src/ApplicativeTests.fs +++ b/tests/Rust/tests/src/ApplicativeTests.fs @@ -3,67 +3,67 @@ module Fable.Tests.ApplicativeTests open Util.Testing let inline (|HasLength|) x = - fun () -> (^a: (member Length: int) x) + fun () -> (^a: (member Length: int) x) let inline length (HasLength f) = f() let lengthWrapper (xs:'a list) = length xs let lengthFixed = length [|1; 2; 3|] -// let zipUnsorted (arr1:_[]) (arr2:_[]) = -// let d1 = dict arr1 -// let d2 = dict arr2 -// let res = ResizeArray<_>() -// for kv1 in d1 do -// let v2 = -// if d2.ContainsKey(kv1.Key) then Some(d2[kv1.Key]) -// else None -// res.Add(kv1.Key, (Some kv1.Value, v2)) -// for kv2 in d2 do -// if not (d1.ContainsKey(kv2.Key)) then -// res.Add(kv2.Key, (None, Some kv2.Value)) -// Array.ofSeq res +let zipUnsorted (arr1:_[]) (arr2:_[]) = + let d1 = dict arr1 + let d2 = dict arr2 + let res = ResizeArray<_>() + for kv1 in d1 do + let v2 = + if d2.ContainsKey(kv1.Key) then Some(d2[kv1.Key]) + else None + res.Add(kv1.Key, (Some kv1.Value, v2)) + for kv2 in d2 do + if not (d1.ContainsKey(kv2.Key)) then + res.Add(kv2.Key, (None, Some kv2.Value)) + Array.ofSeq res let isSortedUsing test proj (arr:_[]) = - let rec loop i = - if i = arr.Length then true - else test (proj arr[i-1]) (proj arr[i]) && loop (i+1) - arr.Length = 0 || loop 1 + let rec loop i = + if i = arr.Length then true + else test (proj arr[i-1]) (proj arr[i]) && loop (i+1) + arr.Length = 0 || loop 1 let zipSorted (arr1:('k*'v1)[]) (arr2:('k*'v2)[]) = - let mutable i1 = 0 - let mutable i2 = 0 - let inline (<.) (a:'k) (b:'k) = compare a b < 0 - let inline eq (a:'k) (b:'k) = compare a b = 0 - let res = ResizeArray<_>() - while i1 < arr1.Length && i2 < arr2.Length do - let (k1, v1), (k2, v2) = arr1[i1], arr2[i2] - if eq k1 k2 then - res.Add(k1, (Some v1, Some v2)) - i1 <- i1 + 1 - i2 <- i2 + 1 - elif k1 <. k2 then - res.Add(k1, (Some v1, None)) - i1 <- i1 + 1 - elif k2 <. k1 then - res.Add(k2, (None, Some v2)) - i2 <- i2 + 1 - while i1 < arr1.Length do - let k1, v1 = arr1[i1] - res.Add(k1, (Some v1, None)) - i1 <- i1 + 1 - while i2 < arr2.Length do - let k2, v2 = arr2[i2] - res.Add(k2, (None, Some v2)) - i2 <- i2 + 2 - Array.ofSeq res + let mutable i1 = 0 + let mutable i2 = 0 + let inline (<.) (a:'k) (b:'k) = compare a b < 0 + let inline eq (a:'k) (b:'k) = compare a b = 0 + let res = ResizeArray<_>() + while i1 < arr1.Length && i2 < arr2.Length do + let (k1, v1), (k2, v2) = arr1[i1], arr2[i2] + if eq k1 k2 then + res.Add(k1, (Some v1, Some v2)) + i1 <- i1 + 1 + i2 <- i2 + 1 + elif k1 <. k2 then + res.Add(k1, (Some v1, None)) + i1 <- i1 + 1 + elif k2 <. k1 then + res.Add(k2, (None, Some v2)) + i2 <- i2 + 1 + while i1 < arr1.Length do + let k1, v1 = arr1[i1] + res.Add(k1, (Some v1, None)) + i1 <- i1 + 1 + while i2 < arr2.Length do + let k2, v2 = arr2[i2] + res.Add(k2, (None, Some v2)) + i2 <- i2 + 2 + Array.ofSeq res // let zipAny (arr1:('k*'v1)[]) (arr2:('k*'v2)[]) = -// let inline (<=.) (a:'k) (b:'k) = compare a b <= 0 -// let inline (>=.) (a:'k) (b:'k) = compare a b >= 0 -// if isSortedUsing (<=.) fst arr1 && isSortedUsing (<=.) fst arr2 then zipSorted arr1 arr2 -// elif isSortedUsing (>=.) fst arr1 && isSortedUsing (>=.) fst arr2 then Array.rev (zipSorted (Array.rev arr1) (Array.rev arr2)) -// else zipUnsorted arr1 arr2 +// let inline (<=.) (a:'k) (b:'k) = compare a b <= 0 +// let inline (>=.) (a:'k) (b:'k) = compare a b >= 0 +// if isSortedUsing (<=.) fst arr1 && isSortedUsing (<=.) fst arr2 then zipSorted arr1 arr2 +// elif isSortedUsing (>=.) fst arr1 && isSortedUsing (>=.) fst arr2 then Array.rev (zipSorted (Array.rev arr1) (Array.rev arr2)) +// else zipUnsorted arr1 arr2 // type Result<'s, 'f> = // | Ok of 's diff --git a/tests/Rust/tests/src/MiscTests.fs b/tests/Rust/tests/src/MiscTests.fs index bff8e10fb4..3fd2e0a91e 100644 --- a/tests/Rust/tests/src/MiscTests.fs +++ b/tests/Rust/tests/src/MiscTests.fs @@ -1,12 +1,132 @@ module Fable.Tests.MiscTests +open System +open Fable.Core open Util.Testing +open Util2.Extensions + +// let mutable private fn = id + +let [] LITERAL_JSON = """{ + "widget": { + "debug": true, + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": [{ "width": 36, "height": 40 }], + "style": "bold", + "name": "text1", + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + } +}""" + +let ANOTHER_JSON = """{ + "widget": { + "debug": false, + "text": { + "data": "lots of", + "size": [{"width": 5}, { "height": 80 }], + "vOffset": 500 + } + } +}""" + +module MiscTestsHelper = + + let counter = + let mutable i = 0 + fun () -> + i <- i + 1 + i + + type Type = { + a : int + b : int + c : int + d : int + e : int + } + with + static member New(n) = { + a = n + b = n * 2 + c = n * 3 + d = n * 4 + e = counter() // <== should only be called twice + } + + member this.Method (v:bool) = { this with a = this.a * if v then 2 else 3 } + member inline this.MethodI (v:bool) = { this with a = this.a * if v then 2 else 3 } + member this.Method () = { this with a = this.a * 10 } + member inline this.MethodI () = { this with a = this.a * 10 } + + type Vector2<[] 'u> = Vector2 of x: float<'u> * y: float<'u> + with + static member inline ( + ) (Vector2(ax, ay), Vector2(bx, by)) = Vector2(ax + bx, ay + by) + static member inline ( * ) (scalar, Vector2(x, y)) = Vector2(scalar * x, scalar * y) + +// We can have aliases with same name in same file #1662 +module One = + type Id = System.Guid + +module Two = + type Id = System.Guid + +type Base() = + let mutable x = 5 + member this.Mutate i = x <- x + i + member _.Value = x + +// type Test(i) as myself = +// inherit Base() +// let mutable y = 12 +// do myself.Mutate(i+2) +// do myself.Mutate2(i) +// member this.Mutate2 i = y <- y + i +// member _.Value2 = y +// member _.Foo() = myself.Value * 2 + +let log2 (a: string) (b: string) = String.Format("a = {0}, b = {1}", a, b) +let logItem1 = log2 "item1" +let logItem2 = log2 "item2" + +type PartialFunctions() = + member _.logItem1 = log2 "item1" + member _.logItem2 = log2 "item2" + +type MaybeBuilder() = + member _.Bind(x,f) = Option.bind f x + member _.Return v = Some v + member _.ReturnFrom o = o +let maybe = MaybeBuilder() + +let riskyOp x y = + if x + y < 100 then Some(x+y) else None + +let execMaybe a = maybe { + let! b = riskyOp a (a+1) + let! c = riskyOp b (b+1) + return c +} [] type km // Define the measure units [] type mi // as simple types decorated [] type h // with Measure attribute -[] type m -[] type s [] type Measure1 [] type Measure2 = Measure1 @@ -14,144 +134,1271 @@ open Util.Testing type MeasureTest() = member _.Method(x: float) = x -// // Can be used in a generic way -// type Vector3D<[] 'u> = -// { x: float<'u>; y: float<'u>; z: float<'u> } -// static member (+) (v1: Vector3D<'u>, v2: Vector3D<'u>) = -// { x = v1.x + v2.x; y = v1.y + v2.y; z = v1.z + v2.z } +// Can be used in a generic way +type Vector3D<[] 'u> = + { x: float<'u>; y: float<'u>; z: float<'u> } + static member (+) (v1: Vector3D<'u>, v2: Vector3D<'u>) = + { x = v1.x + v2.x; y = v1.y + v2.y; z = v1.z + v2.z } -type MyRecord<'a> = - { Value: 'a } - // Check that F# is not automatically assigning 'a name to the argument's generic parameter - static member Stringify v = v +let lerp x (alpha: float<'u>) (delta: MiscTestsHelper.Vector2<'u>) = x + alpha * delta -type TestUnion = - | UncurryUnion of add: (int -> int -> int) +// type PointWithCounter(a: int, b: int) = +// // A variable i. +// let mutable i = 0 +// // A let binding that uses a pattern. +// let (x, y) = (a, b) +// // A private function binding. +// let privateFunction x y = x * x + 2*y +// // A static let binding. +// static let mutable count = 0 +// // A do binding. +// do count <- count + 1 +// member this.Prop1 = x +// member this.Prop2 = y +// member this.CreatedCount = count +// member this.FunctionValue = privateFunction x y -let applyUncurryUnion x y = function - | UncurryUnion f -> f x y -type TestClass(add: (int -> int -> int)) = - member _.Add(x, y) = add x y +let inline f x y = x + y -module ModuleBindings = - let inc1 (x: byref) = - x <- x + 1 - let inc2 x = - x + 1 - let modx = 3 - let mutable mody = 4 +let inline sum x = fun y -> x + y + +module FooModule = + let mutable genericValueBackend = 0 + let inline genericValue<'T> = genericValueBackend <- genericValueBackend + 1; 5 + let add x y = x + y + + type FooInline() = + member _.Bar = "Bar" + member val Value = 0uy with get, set + member inline self.Foo = "Foo" + self.Bar + member inline self.Foofy(i) = String.replicate i self.Bar + member inline this.PropertyInline + with get() = this.Value + and set(v: uint8) = this.Value <- v + +type FooModule.FooInline with + member inline self.Bar2 = "Bar" + self.Bar + member inline self.FoofyPlus(i) = self.Foofy(i * 2) + +let counter = + let mutable i = 0 + fun () -> + i <- i + 1 + i + +type Type = { + a : int + b : int + c : int + d : int + e : int +} +with + static member New(n) = { + a = n + b = n * 2 + c = n * 3 + d = n * 4 + e = counter() // <== should only be called twice + } + + member this.Method (v:bool) = { this with a = this.a * if v then 2 else 3 } + member inline this.MethodI (v:bool) = { this with a = this.a * if v then 2 else 3 } + member this.Method () = { this with a = this.a * 10 } + member inline this.MethodI () = { this with a = this.a * 10 } + +let f1 x y z = x + y + z +let f2 x = x + x + +let f3 () = 5 + +type MyDelegate = Func + +let mutable myMutableField = 0 + +let f4 i = myMutableField <- i +let f5 () = myMutableField <- 5 +let f6 i j = myMutableField <- i * j +let f7 i () = myMutableField <- i * 3 + +type DisposableFoo() = + member _.Foo() = 5 + interface IDisposable with + member _.Dispose () = () + +// type DisposableBar(v) = +// do v := 10 +// interface IDisposable with +// member _.Dispose () = v := 20 + +// let createCellDiposable cell = +// cell := 10 +// { new System.IDisposable with +// member x.Dispose() = cell := 20 } + +let (|NonEmpty|_|) (s: string) = + match s.Trim() with "" -> None | s -> Some s + +type IFoo = + abstract Bar: s: string * [] rest: obj[] -> string + +type IFoo2 = + abstract Value: int with get, set + abstract Test: int -> int + abstract MakeFoo: unit -> IFoo + +// type Foo(i) = +// let mutable j = 5 +// member x.Value = i + j +// member x.MakeFoo2() = { +// new IFoo2 with +// member x2.Value +// with get() = x.Value * 2 +// and set(i) = j <- j + i +// member x2.Test(i) = x2.Value - i +// member x2.MakeFoo() = { +// new IFoo with +// member x3.Bar(s: string, [] rest: obj[]) = +// sprintf "%s: %i %i %i" s x.Value x2.Value j +// } +// } + +type IRenderer = + abstract member doWork: unit -> string + +// type MyComponent(name) as self = +// let work i = sprintf "%s-%i" name i +// let create2 () = { new IRenderer with member _.doWork () = work 2 } +// let create3 = { new IRenderer with member _.doWork () = work 3 } +// let create4 = { new IRenderer with member _.doWork () = self.Work 4 } +// let create5() = { new IRenderer with member _.doWork () = self.Work 5 } +// member _.Work i = work i +// member _.works1 () = { new IRenderer with member _.doWork () = work 1 } +// member _.works2 () = create2() +// member _.works3 () = create3 +// member _.works4 () = create4 +// member _.works5 () = create5() + +type IFoo3 = + abstract Bar: int with get, set + +type SomeClass(name: string) = + member x.Name = name + +type AnotherClass(value: int) = + member x.Value = value + +module NestedModule = + type AnotherClass(value: int) = + member x.Value = value + 5 + +// type INum = abstract member Num: int +// let inline makeNum f = { new INum with member _.Num = f() } + +// type TestClass(n) = +// let addOne x = x + 4 +// let inner = makeNum (fun () -> addOne n) +// member _.GetNum() = inner.Num + +// type RecursiveType(subscribe) as self = +// let foo = 3 +// let getNumber() = 3 +// do subscribe (getNumber >> self.Add2) +// member _.Add2(i) = self.MultiplyFoo(i) + 2 +// member _.MultiplyFoo(i) = i * foo + +// type InliningMutationTest(l: int, r: int) = +// let mutable left = 0 + +// let call() = +// left <- l +// r + +// member _.Run() = +// let right = call() +// left + right + +// module Extensions = +// type IDisposable with +// static member Create(f) = +// { new IDisposable with +// member _.Dispose() = f() } + +// type SomeClass with +// member x.FullName = sprintf "%s Smith" x.Name +// member x.NameTimes (i: int, j: int) = String.replicate (i + j) x.Name + +// type AnotherClass with +// member x.FullName = sprintf "%i" x.Value +// member x.Overload(i: int) = i * 4 +// member x.Overload(s: string) = s + s +// member x.Value2 = x.Value * 2 + +// type NestedModule.AnotherClass with +// member x.Value2 = x.Value * 4 + +// [] +// type ObjectExprBase (x: int ref) as this = +// do x := this.dup x.contents +// abstract member dup: int -> int + +// open Extensions + + +module StyleBuilderHelper = + type StyleBuilderHelper = { TopOffset : int; BottomOffset : int } + type DomBuilder = { ElementType : string; StyleBuilderHelper : StyleBuilderHelper } + let test() = + let helper = { TopOffset = 1; BottomOffset = 2 } + let builder1 = { ElementType = "test"; StyleBuilderHelper = helper } + let builder2 = { builder1 with StyleBuilderHelper = { builder1.StyleBuilderHelper with BottomOffset = 3 } } + match builder1, builder2 with + | { StyleBuilderHelper = { BottomOffset = 2 } }, + { StyleBuilderHelper = { TopOffset = 1; BottomOffset = 3 } } -> true + | _ -> false + +module Mutable = + let mutable prop = 10 + +module Same = + let a = 5 + module Same = + module Same = + let a = 10 + let shouldEqual5 = a + let shouldEqual10 = Same.a + + let private Same = 20 + let shouldEqual20 = Same + let shouldEqual30 = let Same = 25 in Same + 5 + + +// let f8 a b = a + b +// let mutable a = 10 + +// module B = +// let c = a +// a <- a + 5 +// let mutable a = 20 +// let d = f8 2 2 +// let f8 a b = a - b + +// module D = +// let d = a +// a <- a + 5 +// let e = f8 2 2 + +module Internal = + let internal add x y = x + y + + type internal MyType = + static member Subtract x y = x - y + static member Add(?x: int, ?y: int) = + let x = defaultArg x 20 + let y = defaultArg y 50 + x + y + +type MyEnum = + | One = 1 + | Two = 2 + +type TestRef = TestRef of bool ref + +// let delay (f:unit -> unit) = f + +// let mutable mutableValue = 0 + +// let rec recursive1 = delay (fun () -> recursive2()) +// and recursive2 = +// mutableValue <- 5 +// fun () -> mutableValue <- mutableValue * 2 + +let empty<'a> = [Unchecked.defaultof<'a>] + +type IInterface = + abstract member Member : thing1:string -> thing2:string -> string + +type Taster = + abstract Starter: float + abstract Taste: quality: float * quantity: float -> int + +type Eater = + abstract Bite: unit -> int + +let taste (com: Taster) qlty qty = + com.Starter * qlty + qty |> int + +module private MyPrivateModule = + let private bar = "bar" + let publicFoo() = sprintf "foo %s" bar + type Concrete() = + interface IInterface with + member this.Member (thing1: string) (thing2: string) = + sprintf "%s %s" thing2 thing1 + +module Trampoline = + type Result<'a, 'b> = + | Continue of 'a + | Break of 'b + let run func arg = + let mutable state = arg + let mutable result = None + while result.IsNone do + match func state with + | Continue r -> state <- r + | Break r -> result <- Some r + result.Value + +let setCellBuggy x: (int*int) option = + Option.map (fun (x, y) -> max (x - 1) 0, y) x + +let setCell x: (int*int) option = + let max = max + Option.map (fun (x, y) -> max (x - 1) 0, y) x -open ModuleBindings +type Shape = + | Circle of int + | Square of int + | Rectangle of int * int + +type StaticClass = + // static member DefaultParam([] value: bool) = value + // static member DefaultNullParam([] x: obj) = x + static member inline Add(x: int, ?y: int) = + x + (defaultArg y 2) + +type ValueType = + struct + val public X : int + end + +type MutableFoo = + { mutable x: int } + +let incByRef (a: int) (b: byref) = b <- a + b +let addInRef (a: int) (b: inref) = a + b +let setOutRef (a: int) (b: outref) = b <- a + +let mutable mutX = 3 + +// open FSharp.UMX + +// [] type customerId +// [] type orderId +// [] type kg + +// type Order = +// { +// id : string +// customer : string +// quantity : int +// } + +// #if !FABLE_COMPILER_JAVASCRIPT +// type LiteralJson = Fable.JsonProvider.Generator +// #endif + +let inline inlineLambdaWithAnonRecord callback = + fun () -> {| A = 1 |} |> callback + +let sideEffect() = () + +let inline inlineToString (f: 'T -> string): 'T -> string = + let unused = f + fun a -> sprintf "%A" a [] -let ``Passing byref works`` () = - let mutable x = 5 - inc1 &x - let y = inc2 x - x |> equal 6 - y |> equal 7 +let ``Generic unit args work`` () = // #3584 + let to_str = inlineToString (fun (props: unit) -> "s") + to_str () |> equal (sprintf "%A" ()) + +// #if FABLE_COMPILER +// #if !FABLE_COMPILER_JAVASCRIPT +// [] +// let ``Fable.JsonProvider works`` () = +// let parsed = LiteralJson(ANOTHER_JSON) +// parsed.widget.debug |> equal false +// parsed.widget.text.data |> equal "lots of" +// parsed.widget.text.size[1].height |> equal 80. +// parsed.widget.text.vOffset |> equal 500. +// #endif + +[] +let ``Lambdas returning member expression accessing JS object work`` () = // #2311 + let x = inlineLambdaWithAnonRecord (fun x -> x.A) + x() |> equal 1 + +// [] +// let ``Can check compiler version with constant`` () = +// let mutable x = 0 +// #if FABLE_COMPILER +// x <- x + 1 +// #endif +// #if FABLE_COMPILER_3 +// x <- x + 2 +// #endif +// #if FABLE_COMPILER_4 +// x <- x + 4 +// #endif +// #if FABLE_COMPILER_5 +// x <- x + 16 +// #endif +// equal 5 x + +// [] +// let ``Can check compiler version at runtime`` () = +// Compiler.majorMinorVersion >= 4.0 |> equal true +// Text.RegularExpressions.Regex.IsMatch(Compiler.version, @"^\d+\.\d+") |> equal true + +// [] +// let ``Can access compiler options`` () = +// let canAccessOpts = +// match box Compiler.debugMode, box Compiler.typedArrays with +// | :? bool, :? bool -> true +// | _ -> false +// equal true canAccessOpts + +// [] +// let ``Can access extension for generated files`` () = +// Compiler.extension.EndsWith(".js") |> equal true +// #endif [] -let ``Module let bindings work`` () = - mody <- mody + 1 - let z = modx + mody - z |> equal 8 +let ``Values of autogenerated functions are not replaced by optimizations`` () = // See #1583 + let model = Some (5, 5) + let model1 = setCellBuggy model + let model2 = setCell model + model1 = model2 |> equal true [] -let ``Units of measure work`` () = - let a = 4 - let b = 2 - let c = a / b - c |> equal (2) +let ``Assignment block as expression is optimized`` () = + let foo x y = x - y + let mutable x = 15 + let res = A.C.Helper.Add5(let mutable x = 2 in let mutable y = 3 in x + y) + let test () = + A.C.Helper.Add5(let mutable x = 4 in let mutable y = 3 in x + y) + |> equal 12 + test() + equal 10 res + foo x 5 |> equal 10 + +[] +let ``Optimized assignment blocks inside try ... with work`` () = + let res = + try A.C.Helper.Add5(let mutable x = 2 in let mutable y = 3 in x + y) + with _ -> 1 + equal 10 res + +[] +let ``for .. downto works`` () = // See #411 + let mutable x = "" + for i = 1 to 5 do + x <- x + (string i) + equal "12345" x + let mutable y = "" + for i = 5 downto 1 do + y <- y + (string i) + equal "54321" y + +// [] +// let ``Self references in constructors work`` () = // See #124 +// let t = Test(5) +// equal 12 t.Value +// equal 17 t.Value2 + +// [] +// let ``Using self identifier from class definition in members works`` () = // See #124 +// let t = Test(5) +// t.Foo() |> equal 24 + +[] +let ``Module members from partial functions work`` () = // See #115 + logItem1 "item1" |> equal "a = item1, b = item1" + +[] +let ``Class members from partial functions work`` () = // See #115 + let x = PartialFunctions() + x.logItem1 "item1" |> equal "a = item1, b = item1" + +[] +let ``Local values from partial functions work`` () = // See #115 + let logItem1 = log2 "item1" + let logItem2 = log2 "item2" + logItem1 "item1" |> equal "a = item1, b = item1" [] -let ``Units of measure work II`` () = +let ``Custom computation expressions work`` () = + execMaybe 5 |> equal (Some 23) + execMaybe 99 |> equal None + +[] +let ``Units of measure work`` () = 3 + 2 |> equal 5 - // let v1 = { x = 4.3; y = 5.; z = 2.8 } - // let v2 = { x = 5.6; y = 3.8; z = 0. } - // let v3 = v1 + v2 - // equal 8.8 v3.y + + let v1 = { x = 4.3; y = 5.; z = 2.8 } + let v2 = { x = 5.6; y = 3.8; z = 0. } + let v3 = v1 + v2 + equal 8.8 v3.y [] let ``Units of measure work with longs`` () = 3L + 2L |> equal 5L -// [] -// let ``Units of measure work with decimals`` () = -// 3M + 2M |> equal 5M +[] +let ``Units of measure work with decimals`` () = + 3M + 2M |> equal 5M [] -let ``Abbreviated units of measure work`` () = +let ``Abbreviated measures work`` () = // #2313 let x = 5. let c = MeasureTest() c.Method(5.) |> equal x [] -let ``Functions in union fields are uncurried`` () = - let res = UncurryUnion (-) |> applyUncurryUnion 5 2 - res |> equal 3 +let ``Can resolve trait calls from another file with units of measure`` () = // See #2880 + let expected = MiscTestsHelper.Vector2 (8.209999999999999, 7.72) + let x = MiscTestsHelper.Vector2(4.5, 4.5) + MiscTestsHelper.Vector2(5.3, 4.6) |> lerp x 0.7 |> equal expected + +// [] +// let ``FSharp.UMX works`` () = +// let lookupById (orders : Order list) (id : string) = +// orders |> List.tryFind (fun o -> o.id = id) +// let order = +// { +// id = % "orderId" +// customer = % "customerId" +// quantity = % 42 +// } +// // lookupById [] order.customer // compiler error +// let orders = [{ order with quantity = %50 }] +// lookupById orders order.id +// |> Option.map (fun o -> UMX.untag o.quantity) +// |> equal (Some 50) + +// [] +// let ``FSharp.UMX: reflection info`` () = +// let fields = Reflection.FSharpType.GetRecordFields typeof +// fields.Length |> equal 3 + +// [] +// let ``Static constructors work`` () = +// let point1 = PointWithCounter(10, 52) +// sprintf "%d %d %d %d" (point1.Prop1) (point1.Prop2) (point1.CreatedCount) (point1.FunctionValue) +// |> equal "10 52 1 204" +// let point2 = PointWithCounter(20, 99) +// sprintf "%d %d %d %d" (point2.Prop1) (point2.Prop2) (point2.CreatedCount) (point2.FunctionValue) +// |> equal "20 99 2 598" + +// [] +// let ``File with single type in namespace compiles`` () = +// equal SingleTypeInNamespace.Hello "Hello" + +// [] +// let ``Type abbreviation in namespace compiles`` () = // See #140 +// let h = Util2.H(5) +// equal "5" h.Value [] -let ``Functions in class fields are uncurried`` () = - let adder = TestClass((+)) - let res = adder.Add(2, 3) - res |> equal 5 +let ``Multiple namespaces in same file work`` () = // See #1218 + A.C.Helper.Add5(9) |> equal 14 [] -let ``automatically generated generic names don't conflict`` () = - MyRecord.Stringify 456 - |> equal 456 +let ``Inline methods work`` () = + f 2 3 |> equal 5 -#if FABLE_COMPILER_RUST -// open Fable.Core -open Fable.Core.Rust +[] +let ``Inline methods with this argument work`` () = // See #638 + let x = FooModule.FooInline() + x.Foo |> equal "FooBar" + x.Foofy 4 |> equal "BarBarBarBar" -[] -type StructC = { x: int; y: int } +[] +let ``Inline properties work`` () = + let x = FooModule.FooInline() + x.PropertyInline <- 3uy + x.PropertyInline |> equal 3uy -[] [] -let ``Simple outer attribute works`` (): unit = - failwith "Some error" +let ``Inline extension methods with this argument work`` () = // See #638 + let x = FooModule.FooInline() + x.Bar2 |> equal "BarBar" + x.FoofyPlus 3 |> equal "BarBarBarBarBarBar" -[] [] -let ``Name value outer attribute works`` (): unit = - failwith "Some error" +let ``Inline extension methods in other files can be found`` () = // See #1667 + "HOLA CARACOLA".StartsWith("hola") |> equal false + "HOLA CARACOLA".StartsWithIgnoreCase("hola") |> equal true -[] [] -let ``Delimited outer attribute works`` (): unit = - failwith "Some error" +let ``Inline overloaded methods work`` () = + let res1 = Type.New(5).Method(false).Method(true).Method() + let res2 = Type.New(5).MethodI(false).MethodI(true).MethodI() + equal res1.a res2.a + counter() |> equal 3 -// [] -// let f_async () = 2 +[] +let ``Inline overloaded methods in other files work`` () = + let res1 = MiscTestsHelper.Type.New(5).Method(false).Method(true).Method() + let res2 = MiscTestsHelper.Type.New(5).MethodI(false).MethodI(true).MethodI() + equal res1.a res2.a + MiscTestsHelper.counter() |> equal 3 -[] -let f_const () = 3 +[] +let ``Inlined arguments with delayed resolution are only evaluated once`` () = + let mutable x = 0 + let foo() = + x <- x + 1 + 10 + let f = sum (foo()) + f 20 |> f |> equal 40 + equal 1 x + +// [] +// let ``Don't inline values that evaluate multiple times`` () = +// let li = [1;2;3;4] +// let res = List.map (FooModule.add FooModule.genericValue) li +// equal 1 FooModule.genericValueBackend +// equal [6;7;8;9] res + +[] +let ``Calls to core lib from a subfolder work`` () = + Util2.Helper.Format("{0} + {0} = {1}", 2, 4) + |> equal "2 + 2 = 4" + +[] +let ``Conversion to delegate works`` () = + (System.Func<_,_,_,_> f1).Invoke(1,2,3) |> equal 6 + + let f = f1 + (System.Func<_,_,_,_> f).Invoke(1,2,3) |> equal 6 + + let del = System.Func<_,_,_,_>(fun x y z -> x + y + z) + del.Invoke(1,2,3) |> equal 6 + + (System.Func<_,_> f2).Invoke(2) |> equal 4 -[] -let f_unsafe () = 4 + // See #2400 + let func1 : Func = Func(fun () -> 8) + func1.Invoke() |> equal 8 -[] -let f_extern () = 5 + let fn2 () = 9 + let func2 : Func = Func(fn2) + func2.Invoke() |> equal 9 -// [] -// let await x = nativeOnly + let func2b = Func(fn2) + func2b.Invoke() |> equal 9 -// [] -// let ``Async attribute works`` (): unit = -// f_async () |> await |> equal 2 + let fn2c () () = 9 + let func2c : Func = Func(fn2c()) + func2c.Invoke() |> equal 9 -let ``Const attribute works`` (): unit = - f_const () |> equal 3 + let fn3 i = i + 4 + let func3 = Func(fn3) + func3.Invoke(7) |> equal 11 -[] -let ``Unsafe attribute works`` (): unit = - f_unsafe () |> equal 4 + let fn4 x y = x * y - 3 + let func4 = Func(fn4) + func4.Invoke(4, 6) |> equal 21 -let ``Extern attribute works`` (): unit = - f_extern () |> equal 5 +[] +let ``Conversion to Func<_> works`` () = + (System.Func<_> f3).Invoke() |> equal 5 + let f = Func<_>(fun () -> 6) + f.Invoke() |> equal 6 + +[] +let ``Conversion to aliased Func<_> works`` () = + (MyDelegate f3).Invoke() |> equal 5 + let f = MyDelegate(fun () -> 6) + f.Invoke() |> equal 6 + +// TODO +// [] +// let ``Conversion to FSharpFunc<_,_,_> works`` () = +// let f x y = x + y +// let f = FSharpFunc<_,_,_>.Adapt(f) +// f.Invoke(1, 2) |> equal 3 + +// [] +// let ``Conversion to FSharpFunc<_,_,_,_> works`` () = +// let f x y z = x + y + z +// let f = FSharpFunc<_,_,_,_>.Adapt(f) +// f.Invoke(1, 2, 3) |> equal 6 + +[] +let ``Conversion to Action<_> works`` () = + let f1' = Action(fun i -> myMutableField <- i * 2) + let f2' = Action(f4) + let f3' = Action<_>(f6 4) + f1'.Invoke(1) + equal 2 myMutableField + f2'.Invoke(8) + equal 8 myMutableField + f3'.Invoke(10) + equal 40 myMutableField -#endif //FABLE_COMPILER_RUST +// TODO!!! +// [] +// let ``Conversion to Action works`` () = +// let f4' = Action(fun () -> myMutableField <- 7) +// let f5' = Action(f5) +// let f6' = Action(f7 3) +// let f7' i () = myMutableField <- i * 3 +// let f8' = Action(f7' 3) +// f4'.Invoke() +// equal 7 myMutableField +// f5'.Invoke() +// equal 5 myMutableField +// f6'.Invoke() +// equal 9 myMutableField +// f8'.Invoke() +// equal 9 myMutableField + +[] +let ``Multiple active pattern calls work`` () = + match " Hello ", " Bye " with + | NonEmpty "Hello", NonEmpty "Bye" -> true + | _ -> false + |> equal true + +// [] +// let ``ParamArray in object expression works`` () = +// let o = { new IFoo with member x.Bar(s: string, [] rest: obj[]) = String.Format(s, rest) } +// o.Bar("{0} + {0} = {1}", 2, 4) +// |> equal "2 + 2 = 4" + +// [] +// let ``Object expression can reference enclosing type and self`` () = // See #158 +// let f = Foo(5) +// let f2 = f.MakeFoo2() +// f2.Value <- 2 +// f.Value |> equal 12 +// f2.Test(2) |> equal 22 + +// [] +// let ``Nested object expressions work`` () = // See #158 +// let f = Foo(5) +// let f2 = f.MakeFoo2() +// f2.MakeFoo().Bar("Numbers") |> equal "Numbers: 10 20 5" + +// [] +// let ``References to enclosing type from object expression work`` () = // See #438 +// MyComponent("TestA").works1().doWork() |> equal "TestA-1" +// MyComponent("TestB").works2().doWork() |> equal "TestB-2" +// MyComponent("TestC").works3().doWork() |> equal "TestC-3" +// MyComponent("TestD").works4().doWork() |> equal "TestD-4" +// MyComponent("TestE").works5().doWork() |> equal "TestE-5" + +// [] +// let ``Properties in object expression work`` () = +// let mutable backend = 0 +// let o = { new IFoo3 with member x.Bar with get() = backend and set(v) = backend <- v } +// o.Bar |> equal 0 +// backend <- 5 +// o.Bar |> equal 5 +// o.Bar <- 10 +// o.Bar |> equal 10 + +// [] +// let ``Object expression from class works`` () = +// let o = { new SomeClass("World") with member x.ToString() = sprintf "Hello %s" x.Name } +// // TODO: Type testing for object expressions? +// // match box o with +// // | :? SomeClass as c -> c.ToString() +// // | _ -> "Unknown" +// // |> equal "Hello World" +// o.ToString() |> equal "Hello World" + +// [] +// let ``Inlined object expression doesn't change argument this context`` () = // See #1291 +// let t = TestClass(42) +// t.GetNum() |> equal 46 + +// [] +// let ``Object expressions don't optimize members away`` () = // See #1434 +// let o = { +// new Taster with +// member _.Starter = 5.5 +// member this.Taste(quality, quantity) = +// taste this quality quantity +// interface Eater with +// member _.Bite() = 25 +// } +// o.Taste(4., 6.) |> equal 28 + +// [] +// let ``Members are accessible in abstract class constructor inherited by object expr`` () = // See #2139 +// let x = ref 5 +// let obj = { new ObjectExprBase(x) with +// override _.dup x = x * x } +// equal 25 x.contents + +// [] +// let ``Composition with recursive `this` works`` () = +// let mutable x = 0 +// RecursiveType(fun f -> x <- f()) |> ignore +// equal 11 x + +// [] +// let ``Type extension static methods work`` () = +// let disposed = ref false +// let disp = IDisposable.Create(fun () -> disposed := true) +// disp.Dispose () +// equal true !disposed + +// [] +// let ``Type extension properties work`` () = +// let c = SomeClass("John") +// equal "John Smith" c.FullName + +// [] +// let ``Type extension methods work`` () = +// let c = SomeClass("John") +// c.NameTimes(1,2) |> equal "JohnJohnJohn" + +// [] +// let ``Type extension methods with same name work`` () = +// let c = AnotherClass(3) +// equal "3" c.FullName + +// [] +// let ``Type extension overloads work`` () = +// let c = AnotherClass(3) +// c.Overload("3") |> equal "33" +// c.Overload(3) |> equal 12 + +// [] +// let ``Extending different types with same name and same method works`` () = +// AnotherClass(5).Value2 |> equal 10 +// NestedModule.AnotherClass(5).Value2 |> equal 40 + +[] +let ``Module, members and properties with same name don't clash`` () = + StyleBuilderHelper.test() |> equal true + +[] +let ``Module mutable properties work`` () = + equal 10 Mutable.prop + Mutable.prop <- 5 + equal 5 Mutable.prop + +[] +let ``Accessing members of parent module with same name works`` () = + equal 5 Same.Same.shouldEqual5 + +[] +let ``Accessing members of child module with same name works`` () = + equal 10 Same.Same.shouldEqual10 + +[] +let ``Accessing members with same name as module works`` () = + equal 20 Same.Same.shouldEqual20 + +[] +let ``Naming values with same name as module works`` () = + equal 30 Same.Same.shouldEqual30 + +[] +let ``Can access nested recursive function with mangled name`` () = + Util.Bar.nestedRecursive 3 |> equal 10 + +[] +let ``Can access non nested recursive function with mangled name`` () = + Util.nonNestedRecursive "ja" |> equal "jajaja" + +[] +let ``Module members don't conflict with JS names`` () = + Util.Int32Array |> Array.sum |> equal 3 + +[] +let ``Modules don't conflict with JS names`` () = + Util.Float64Array.Float64Array |> Array.sum |> equal 7. + +// Modules and TestFixtures bind values in a slightly different way +// Test both cases +// [] +// let ``Binding doesn't shadow top-level values`` () = // See #130 +// equal 10 Util.B.c +// equal 20 Util.B.D.d + +// [] +// let ``Binding doesn't shadow top-level values (TestFixture)`` () = // See #130 +// equal 10 B.c +// equal 20 B.D.d + +// [] +// let ``Binding doesn't shadow top-level functions`` () = // See #130 +// equal 4 Util.B.d +// equal 0 Util.B.D.e + +// [] +// let ``Binding doesn't shadow top-level functions (TestFixture)`` () = // See #130 +// equal 4 B.d +// equal 0 B.D.e + +// [] +// let ``Setting a top-level value doesn't alter values at same level`` () = // See #130 +// equal 15 Util.a +// equal 25 Util.B.a + +// [] +// let ``Setting a top-level value doesn't alter values at same level (TestFixture)`` () = // See #130 +// equal 15 a +// equal 25 B.a + +[] +let ``Internal members can be accessed from other modules`` () = // See #163 + Internal.add 3 2 |> equal 5 + +[] +let ``Internal types can be accessed from other modules`` () = // See #163 + Internal.MyType.Subtract 3 2 |> equal 1 + +// In F# both branches of if-then-else has the same type, +// but this is not always true in Fable AST. For example when +// one branch is a Throw expression, it has always type Unit. +// So we should test that the type of the whole expression is not determined +// by the throw expression in one of its branches. +// +// The same applies to try-with expression. + +// #if FABLE_COMPILER +// // This test fails if the system language is set to other language than English +// [] +// let ``Pattern-matching against discriminated unions gives proper error message`` () = +// try +// let unitCircle = Circle 1 +// match unitCircle with +// | Rectangle(n, m) -> failwith "Should not happen" +// | Square n -> failwith "Should not happen" +// with +// | ex -> ex.Message.StartsWith "Match failure" |> equal true +// #endif + +[] +let ``Type of if-then-else expression is correctly determined when 'then' branch throws`` () = + let f () = + if false then failwith "error" else 7 + f () |> equal 7 + +[] +let ``Type of if-then-else expression is correctly determined when 'else' branch throws`` () = + let f () = + if true then 7 else failwith "error" + f () |> equal 7 + +[] +let ``Type of try-with expression is correctly determined when 'try' block throws`` () = + doesntThrow (fun () -> + let f () = + try failwith "error" with | _ -> 7 + f () |> equal 7 + ) + +[] +let ``Type of try-with expression is correctly determined when exception handler throws`` () = + doesntThrow (fun () -> + let f () = + try 7 with | _ -> failwith "error" + f () |> equal 7 + ) +[] +let ``use doesn't return on finally clause`` () = // See #211 + let foo() = + use c = new DisposableFoo() + c.Foo() + foo() |> equal 5 + +// [] +// let ``use calls Dispose at the end of the scope`` () = +// let cell = ref 0 +// let res = +// use c = new DisposableBar(cell) +// !cell +// res |> equal 10 +// !cell |> equal 20 + +// [] +// let ``use calls Dispose (of an object expression) at the end of the scope`` () = +// let cell = ref 0 +// let res = +// use c = createCellDiposable cell +// !cell +// res |> equal 10 +// !cell |> equal 20 + +// [] +// let ``Can use `use` with null`` () = // See #2719 +// let mutable x = 0 +// let msg = +// use __ = null : IDisposable +// "hooray" +// msg |> equal "hooray" +// x |> equal 0 + +// let msg = +// use __ = { new IDisposable with member _.Dispose() = x <- 1 } +// "booh" +// msg |> equal "booh" +// x |> equal 1 + +[] +let ``Unchecked.defaultof works`` () = + Unchecked.defaultof |> equal 0 + Unchecked.defaultof |> equal 0L + Unchecked.defaultof |> equal 0I + Unchecked.defaultof |> equal 0M + Unchecked.defaultof |> equal DateTime.MinValue + Unchecked.defaultof |> equal (TimeSpan.FromMilliseconds 0.) + Unchecked.defaultof |> equal false + Unchecked.defaultof |> equal null + Unchecked.defaultof |> equal Guid.Empty + let x = ValueType() + Unchecked.defaultof |> equal x + x.X |> equal 0 + +[] +let ``Unchecked.defaultof works with tuples`` () = // See #2491 + // TODO: Non-struct tuples + // let y: (int*int) = Unchecked.defaultof<_> + // equal null (box y) + let x: struct (int*int) = Unchecked.defaultof<_> + equal (struct (0, 0)) x + +[] +let ``Pattern matching optimization works (switch statement)`` () = + let mutable x = "" + let i = 4 + match i with + | 1 -> x <- "1" + | 2 -> x <- "2" + | 3 | 4 -> x <- "3" // Multiple cases are allowed + // | 5 | 6 as j -> x <- string j // This prevents the optimization + | 4 -> x <- "4" // Unreachable cases are removed + | _ -> x <- "?" + equal "3" x + + match "Bye" with + | "Hi" -> x <- "Bye" + | "Bye" -> let h = "Hi" in x <- sprintf "%s there!" h + | _ -> x <- "?" + equal "Hi there!" x + + // Pattern matching with Int64/UInt64 is not converted to switch + match 2L with + | 1L -> x <- "1L" + | 2L -> x <- "2L" + | _ -> x <- "?" + equal "2L" x + + // Pattern matching with boolean is not converted to switch + match false with + | true -> x <- "True" + | false -> x <- "False" + equal "False" x + + match MyEnum.One with + | MyEnum.One -> x <- "One" + | MyEnum.Two -> x <- "Two" + | _ -> failwith "never" + equal "One" x + +[] +let ``Pattern matching optimization works (switch expression)`` () = + let i = 4 + match i with + | 1 -> "1" + | 2 -> "2" + | 3 | 4 -> "3" + | _ -> "?" + |> equal "3" + + match "Bye" with + | "Hi" -> "Bye" + | "Bye" -> let h = "Hi" in sprintf "%s there!" h + | _ -> "?" + |> equal "Hi there!" + + match MyEnum.One with + | MyEnum.One -> "One" + | MyEnum.Two -> "Two" + | _ -> failwith "never" + |> equal "One" + +[] +let ``Pattern matching with same result for last pattern and wildcard works`` () = // #2357 + let x = 10 + let y = + match x with + | 1 + | 2 + | 3 + | 4 + | _ -> + sideEffect() + 5 + equal 5 y + +[] +let ``FSharpRef can be used in properties`` () = // See #521 + let r = ref false + let x = TestRef r + r := true + match x with TestRef r2 -> !r2 + |> equal true + +// [] +// let ``Recursive values work`` () = // See #237 +// mutableValue |> equal 5 +// recursive1() +// mutableValue |> equal 10 + +// [] +// let ``Module generic methods without arguments work`` () = +// let li = empty +// Seq.length li |> equal 1 + +[] +let ``Public members of private modules can be accessed`` () = // See #696 + MyPrivateModule.publicFoo() |> equal "foo bar" + +[] +let ``Public types of private modules can be accessed`` () = // See #841 + let thing = MyPrivateModule.Concrete() :> IInterface + thing.Member "World" "Hello" |> equal "Hello World" + +// [] +// let ``Types declared in signature file work`` () = // See #754 +// let t = Spaces.TRec.Create("haha", "hoho") +// t.Value |> equal "hahahoho" + +// [] +// let ``Primary constructor of types from signature files work`` () = // See #571 +// Spaces.Test(true).Status |> equal true +// Spaces.Test(false).Status |> equal false + +// [] +// let ``Two types with same name in different folders work`` () = // See #781 +// tempet.SayA.hello "Albert" +// |> equal "Hello Albert from SayA" +// tempet.SayB.hello "Albert" +// |> equal "Hello Albert from SayB" + +[] +let ``While with isNone doesn't hang with Some ()`` () = + Trampoline.run (fun _ -> Trampoline.Break "hello") () |> ignore + Trampoline.run (fun _ -> Trampoline.Break 42) () |> ignore + Trampoline.run (fun _ -> Trampoline.Break ()) () |> ignore + +[] +let ``Removing optional arguments not in tail position works`` () = + Internal.MyType.Add(y=6) |> equal 26 + +[] +let ``Inlined methods can have optional arguments`` () = + StaticClass.Add(2, 3) |> equal 5 + StaticClass.Add(5) |> equal 7 + +// [] +// let ``DefaultParameterValue works`` () = +// StaticClass.DefaultParam() |> equal true + +// [] +// let ``DefaultParameterValue works with null`` () = +// StaticClass.DefaultNullParam() |> isNull |> equal true +// StaticClass.DefaultNullParam(5) |> isNull |> equal false + +[] +let ``Ignore shouldn't return value`` () = // See #1360 + let producer () = 7 + equal (box ()) (box(ignore(producer()))) + +// [] +// let ``Can import files specified via globbing patterns`` () = // See #1942 +// Glob.hello "Albert" +// |> equal "Hello Albert from Glob" + +[] +let ``Mutable variables can be passed by reference`` () = + let a = 1 + let mutable b = 2 + incByRef a &b + b |> equal 3 + addInRef a &b |> equal 4 + b |> equal 3 + setOutRef a &b + b |> equal 1 + +// [] +// let ``Public mutable variables can be passed by reference`` () = +// let a = 1 +// mutX <- 2 +// incByRef a &mutX +// mutX |> equal 3 +// addInRef a &mutX |> equal 4 +// mutX |> equal 3 +// setOutRef a &mutX +// mutX |> equal 1 + +[] +let ``Mutable fields can be passed by reference`` () = + let a = 1 + let foo: MutableFoo = { x = 2 } + incByRef a &foo.x + foo.x |> equal 3 + addInRef a &foo.x |> equal 4 + foo.x |> equal 3 + setOutRef a &foo.x + foo.x |> equal 1 + +[] +let ``Assigning to unit works`` () = // See #2548 + let mutable value = 2 + let inline doit x (f: 'A -> 'T) = + let mutable r = Unchecked.defaultof<_> + r <- f x + r + + doit "a" (fun a -> a + "b") + |> equal "ab" + + doit 2 (fun x -> value <- value + x) + value |> equal 4 + +[] +let ``Mutating variables is not postponed (functions)`` () = + let ``inlineData PR #2683`` = [3, 2, 5; 5, 10, 15; 10, 20, 30] + + let runCase (l: int) (r: int) (expect: int) = + let mutable left = 0 + let call() = + left <- l + r + + let run() = + let right = call() + left + right + + run() |> equal expect + + for (l, r, ``l + r``) in ``inlineData PR #2683`` do + runCase l r ``l + r`` + +// [] +// let ``Mutating variables is not postponed (classes)`` () = +// let ``inlineData PR #2683`` = [3, 2, 5; 5, 10, 15; 10, 20, 30] + +// let runCase (l: int) (r: int) (expect: int) = +// InliningMutationTest(l, r).Run() |> equal expect +// InliningMutationTest(l, r).Run() |> equal expect + +// for (l, r, ``l + r``) in ``inlineData PR #2683`` do +// runCase l r ``l + r`` + +// [] +// let ``References captured by >> are eagerly evaluated`` () = // See #2851 +// let times2 x = x * 2 +// fn <- fn >> times2 +// fn 5 |> equal 10 diff --git a/tests/Rust/tests/src/MiscTests2.fs b/tests/Rust/tests/src/MiscTests2.fs index c3b1927299..6b985b772d 100644 --- a/tests/Rust/tests/src/MiscTests2.fs +++ b/tests/Rust/tests/src/MiscTests2.fs @@ -1,133 +1,12 @@ module Fable.Tests.MiscTests2 -open System -// open System.Runtime.InteropServices -open Fable.Core open Util.Testing -open Util2.Extensions - -// let mutable private fn = id - -let [] LITERAL_JSON = """{ - "widget": { - "debug": true, - "window": { - "title": "Sample Konfabulator Widget", - "name": "main_window", - "width": 500, - "height": 500 - }, - "image": { - "src": "Images/Sun.png", - "name": "sun1", - "hOffset": 250, - "vOffset": 250, - "alignment": "center" - }, - "text": { - "data": "Click Here", - "size": [{ "width": 36, "height": 40 }], - "style": "bold", - "name": "text1", - "vOffset": 100, - "alignment": "center", - "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" - } - } -}""" - -let ANOTHER_JSON = """{ - "widget": { - "debug": false, - "text": { - "data": "lots of", - "size": [{"width": 5}, { "height": 80 }], - "vOffset": 500 - } - } -}""" - -module MiscTestsHelper = - - let counter = - let mutable i = 0 - fun () -> - i <- i + 1 - i - - type Type = { - a : int - b : int - c : int - d : int - e : int - } - with - static member New(n) = { - a = n - b = n * 2 - c = n * 3 - d = n * 4 - e = counter() // <== should only be called twice - } - - member this.Method (v:bool) = { this with a = this.a * if v then 2 else 3 } - member inline this.MethodI (v:bool) = { this with a = this.a * if v then 2 else 3 } - member this.Method () = { this with a = this.a * 10 } - member inline this.MethodI () = { this with a = this.a * 10 } - - type Vector2<[] 'u> = Vector2 of x: float<'u> * y: float<'u> - with - static member inline ( + ) (Vector2(ax, ay), Vector2(bx, by)) = Vector2(ax + bx, ay + by) - static member inline ( * ) (scalar, Vector2(x, y)) = Vector2(scalar * x, scalar * y) - -// We can have aliases with same name in same file #1662 -module One = - type Id = System.Guid - -module Two = - type Id = System.Guid - -type Base() = - let mutable x = 5 - member this.Mutate i = x <- x + i - member _.Value = x - -// type Test(i) as myself = -// inherit Base() -// let mutable y = 12 -// do myself.Mutate(i+2) -// do myself.Mutate2(i) -// member this.Mutate2 i = y <- y + i -// member _.Value2 = y -// member _.Foo() = myself.Value * 2 - -let log2 (a: string) (b: string) = String.Format("a = {0}, b = {1}", a, b) -let logItem1 = log2 "item1" -let logItem2 = log2 "item2" - -type PartialFunctions() = - member _.logItem1 = log2 "item1" - member _.logItem2 = log2 "item2" - -type MaybeBuilder() = - member _.Bind(x,f) = Option.bind f x - member _.Return v = Some v - member _.ReturnFrom o = o -let maybe = MaybeBuilder() - -let riskyOp x y = - if x + y < 100 then Some(x+y) else None - -let execMaybe a = maybe { - let! b = riskyOp a (a+1) - let! c = riskyOp b (b+1) - return c -} [] type km // Define the measure units [] type mi // as simple types decorated [] type h // with Measure attribute +[] type m +[] type s [] type Measure1 [] type Measure2 = Measure1 @@ -135,1263 +14,144 @@ let execMaybe a = maybe { type MeasureTest() = member _.Method(x: float) = x -// Can be used in a generic way -type Vector3D<[] 'u> = - { x: float<'u>; y: float<'u>; z: float<'u> } - static member (+) (v1: Vector3D<'u>, v2: Vector3D<'u>) = - { x = v1.x + v2.x; y = v1.y + v2.y; z = v1.z + v2.z } - -let lerp x (alpha: float<'u>) (delta: MiscTestsHelper.Vector2<'u>) = x + alpha * delta - -// type PointWithCounter(a: int, b: int) = -// // A variable i. -// let mutable i = 0 -// // A let binding that uses a pattern. -// let (x, y) = (a, b) -// // A private function binding. -// let privateFunction x y = x * x + 2*y -// // A static let binding. -// static let mutable count = 0 -// // A do binding. -// do count <- count + 1 -// member this.Prop1 = x -// member this.Prop2 = y -// member this.CreatedCount = count -// member this.FunctionValue = privateFunction x y - - -let inline f x y = x + y - -let inline sum x = fun y -> x + y - -module FooModule = - let mutable genericValueBackend = 0 - let inline genericValue<'T> = genericValueBackend <- genericValueBackend + 1; 5 - let add x y = x + y - - type FooInline() = - member _.Bar = "Bar" - member val Value = 0uy with get, set - member inline self.Foo = "Foo" + self.Bar - member inline self.Foofy(i) = String.replicate i self.Bar - member inline this.PropertyInline - with get() = this.Value - and set(v: uint8) = this.Value <- v - -type FooModule.FooInline with - member inline self.Bar2 = "Bar" + self.Bar - member inline self.FoofyPlus(i) = self.Foofy(i * 2) - -let counter = - let mutable i = 0 - fun () -> - i <- i + 1 - i - -type Type = { - a : int - b : int - c : int - d : int - e : int -} -with - static member New(n) = { - a = n - b = n * 2 - c = n * 3 - d = n * 4 - e = counter() // <== should only be called twice - } - - member this.Method (v:bool) = { this with a = this.a * if v then 2 else 3 } - member inline this.MethodI (v:bool) = { this with a = this.a * if v then 2 else 3 } - member this.Method () = { this with a = this.a * 10 } - member inline this.MethodI () = { this with a = this.a * 10 } - -let f1 x y z = x + y + z -let f2 x = x + x - -let f3 () = 5 - -type MyDelegate = Func - -let mutable myMutableField = 0 - -let f4 i = myMutableField <- i -let f5 () = myMutableField <- 5 -let f6 i j = myMutableField <- i * j -let f7 i () = myMutableField <- i * 3 - -type DisposableFoo() = - member _.Foo() = 5 - interface IDisposable with - member _.Dispose () = () - -// type DisposableBar(v) = -// do v := 10 -// interface IDisposable with -// member _.Dispose () = v := 20 - -// let createCellDiposable cell = -// cell := 10 -// { new System.IDisposable with -// member x.Dispose() = cell := 20 } - -let (|NonEmpty|_|) (s: string) = - match s.Trim() with "" -> None | s -> Some s - -type IFoo = - abstract Bar: s: string * [] rest: obj[] -> string - -type IFoo2 = - abstract Value: int with get, set - abstract Test: int -> int - abstract MakeFoo: unit -> IFoo - -// type Foo(i) = -// let mutable j = 5 -// member x.Value = i + j -// member x.MakeFoo2() = { -// new IFoo2 with -// member x2.Value -// with get() = x.Value * 2 -// and set(i) = j <- j + i -// member x2.Test(i) = x2.Value - i -// member x2.MakeFoo() = { -// new IFoo with -// member x3.Bar(s: string, [] rest: obj[]) = -// sprintf "%s: %i %i %i" s x.Value x2.Value j -// } -// } - -type IRenderer = - abstract member doWork: unit -> string - -// type MyComponent(name) as self = -// let work i = sprintf "%s-%i" name i -// let create2 () = { new IRenderer with member _.doWork () = work 2 } -// let create3 = { new IRenderer with member _.doWork () = work 3 } -// let create4 = { new IRenderer with member _.doWork () = self.Work 4 } -// let create5() = { new IRenderer with member _.doWork () = self.Work 5 } -// member _.Work i = work i -// member _.works1 () = { new IRenderer with member _.doWork () = work 1 } -// member _.works2 () = create2() -// member _.works3 () = create3 -// member _.works4 () = create4 -// member _.works5 () = create5() - -type IFoo3 = - abstract Bar: int with get, set - -type SomeClass(name: string) = - member x.Name = name - -type AnotherClass(value: int) = - member x.Value = value - -module NestedModule = - type AnotherClass(value: int) = - member x.Value = value + 5 - -// type INum = abstract member Num: int -// let inline makeNum f = { new INum with member _.Num = f() } - -// type TestClass(n) = -// let addOne x = x + 4 -// let inner = makeNum (fun () -> addOne n) -// member _.GetNum() = inner.Num - -// type RecursiveType(subscribe) as self = -// let foo = 3 -// let getNumber() = 3 -// do subscribe (getNumber >> self.Add2) -// member _.Add2(i) = self.MultiplyFoo(i) + 2 -// member _.MultiplyFoo(i) = i * foo - -// type InliningMutationTest(l: int, r: int) = -// let mutable left = 0 - -// let call() = -// left <- l -// r - -// member _.Run() = -// let right = call() -// left + right - -// module Extensions = -// type IDisposable with -// static member Create(f) = -// { new IDisposable with -// member _.Dispose() = f() } - -// type SomeClass with -// member x.FullName = sprintf "%s Smith" x.Name -// member x.NameTimes (i: int, j: int) = String.replicate (i + j) x.Name - -// type AnotherClass with -// member x.FullName = sprintf "%i" x.Value -// member x.Overload(i: int) = i * 4 -// member x.Overload(s: string) = s + s -// member x.Value2 = x.Value * 2 - -// type NestedModule.AnotherClass with -// member x.Value2 = x.Value * 4 - -// [] -// type ObjectExprBase (x: int ref) as this = -// do x := this.dup x.contents -// abstract member dup: int -> int - -// open Extensions - - -module StyleBuilderHelper = - type StyleBuilderHelper = { TopOffset : int; BottomOffset : int } - type DomBuilder = { ElementType : string; StyleBuilderHelper : StyleBuilderHelper } - let test() = - let helper = { TopOffset = 1; BottomOffset = 2 } - let builder1 = { ElementType = "test"; StyleBuilderHelper = helper } - let builder2 = { builder1 with StyleBuilderHelper = { builder1.StyleBuilderHelper with BottomOffset = 3 } } - match builder1, builder2 with - | { StyleBuilderHelper = { BottomOffset = 2 } }, - { StyleBuilderHelper = { TopOffset = 1; BottomOffset = 3 } } -> true - | _ -> false - -module Mutable = - let mutable prop = 10 - -module Same = - let a = 5 - module Same = - module Same = - let a = 10 - let shouldEqual5 = a - let shouldEqual10 = Same.a - - let private Same = 20 - let shouldEqual20 = Same - let shouldEqual30 = let Same = 25 in Same + 5 - - -// let f8 a b = a + b -// let mutable a = 10 - -// module B = -// let c = a -// a <- a + 5 -// let mutable a = 20 -// let d = f8 2 2 -// let f8 a b = a - b - -// module D = -// let d = a -// a <- a + 5 -// let e = f8 2 2 - -module Internal = - let internal add x y = x + y - - type internal MyType = - static member Subtract x y = x - y - static member Add(?x: int, ?y: int) = - let x = defaultArg x 20 - let y = defaultArg y 50 - x + y - -type MyEnum = - | One = 1 - | Two = 2 - -type TestRef = TestRef of bool ref - -// let delay (f:unit -> unit) = f - -// let mutable mutableValue = 0 - -// let rec recursive1 = delay (fun () -> recursive2()) -// and recursive2 = -// mutableValue <- 5 -// fun () -> mutableValue <- mutableValue * 2 - -let empty<'a> = [Unchecked.defaultof<'a>] - -type IInterface = - abstract member Member : thing1:string -> thing2:string -> string - -type Taster = - abstract Starter: float - abstract Taste: quality: float * quantity: float -> int - -type Eater = - abstract Bite: unit -> int +// // Can be used in a generic way +// type Vector3D<[] 'u> = +// { x: float<'u>; y: float<'u>; z: float<'u> } +// static member (+) (v1: Vector3D<'u>, v2: Vector3D<'u>) = +// { x = v1.x + v2.x; y = v1.y + v2.y; z = v1.z + v2.z } -let taste (com: Taster) qlty qty = - com.Starter * qlty + qty |> int +type MyRecord<'a> = + { Value: 'a } + // Check that F# is not automatically assigning 'a name to the argument's generic parameter + static member Stringify v = v -module private MyPrivateModule = - let private bar = "bar" - let publicFoo() = sprintf "foo %s" bar - type Concrete() = - interface IInterface with - member this.Member (thing1: string) (thing2: string) = - sprintf "%s %s" thing2 thing1 +type TestUnion = + | UncurryUnion of add: (int -> int -> int) -module Trampoline = - type Result<'a, 'b> = - | Continue of 'a - | Break of 'b - let run func arg = - let mutable state = arg - let mutable result = None - while result.IsNone do - match func state with - | Continue r -> state <- r - | Break r -> result <- Some r - result.Value +let applyUncurryUnion x y = function + | UncurryUnion f -> f x y -let setCellBuggy x: (int*int) option = - Option.map (fun (x, y) -> max (x - 1) 0, y) x +type TestClass(add: (int -> int -> int)) = + member _.Add(x, y) = add x y -let setCell x: (int*int) option = - let max = max - Option.map (fun (x, y) -> max (x - 1) 0, y) x - -type Shape = - | Circle of int - | Square of int - | Rectangle of int * int - -type StaticClass = - // static member DefaultParam([] value: bool) = value - // static member DefaultNullParam([] x: obj) = x - static member inline Add(x: int, ?y: int) = - x + (defaultArg y 2) - -type ValueType = - struct - val public X : int - end - -type MutableFoo = - { mutable x: int } - -let incByRef (a: int) (b: byref) = b <- a + b -let addInRef (a: int) (b: inref) = a + b -let setOutRef (a: int) (b: outref) = b <- a - -let mutable mutX = 3 - -// open FSharp.UMX - -// [] type customerId -// [] type orderId -// [] type kg - -// type Order = -// { -// id : string -// customer : string -// quantity : int -// } - -// #if !FABLE_COMPILER_JAVASCRIPT -// type LiteralJson = Fable.JsonProvider.Generator -// #endif - -let inline inlineLambdaWithAnonRecord callback = - fun () -> {| A = 1 |} |> callback - -let sideEffect() = () - - -// #if FABLE_COMPILER -// #if !FABLE_COMPILER_JAVASCRIPT -// [] -// let ``Fable.JsonProvider works`` () = -// let parsed = LiteralJson(ANOTHER_JSON) -// parsed.widget.debug |> equal false -// parsed.widget.text.data |> equal "lots of" -// parsed.widget.text.size[1].height |> equal 80. -// parsed.widget.text.vOffset |> equal 500. -// #endif - -[] -let ``Lambdas returning member expression accessing JS object work`` () = // #2311 - let x = inlineLambdaWithAnonRecord (fun x -> x.A) - x() |> equal 1 - -// [] -// let ``Can check compiler version with constant`` () = -// let mutable x = 0 -// #if FABLE_COMPILER -// x <- x + 1 -// #endif -// #if FABLE_COMPILER_3 -// x <- x + 2 -// #endif -// #if FABLE_COMPILER_4 -// x <- x + 4 -// #endif -// #if FABLE_COMPILER_5 -// x <- x + 16 -// #endif -// equal 5 x - -// [] -// let ``Can check compiler version at runtime`` () = -// Compiler.majorMinorVersion >= 4.0 |> equal true -// Text.RegularExpressions.Regex.IsMatch(Compiler.version, @"^\d+\.\d+") |> equal true - -// [] -// let ``Can access compiler options`` () = -// let canAccessOpts = -// match box Compiler.debugMode, box Compiler.typedArrays with -// | :? bool, :? bool -> true -// | _ -> false -// equal true canAccessOpts - -// [] -// let ``Can access extension for generated files`` () = -// Compiler.extension.EndsWith(".js") |> equal true -// #endif - -[] -let ``Values of autogenerated functions are not replaced by optimizations`` () = // See #1583 - let model = Some (5, 5) - let model1 = setCellBuggy model - let model2 = setCell model - model1 = model2 |> equal true - -[] -let ``Assignment block as expression is optimized`` () = - let foo x y = x - y - let mutable x = 15 - let res = A.C.Helper.Add5(let mutable x = 2 in let mutable y = 3 in x + y) - let test () = - A.C.Helper.Add5(let mutable x = 4 in let mutable y = 3 in x + y) - |> equal 12 - test() - equal 10 res - foo x 5 |> equal 10 - -[] -let ``Optimized assignment blocks inside try ... with work`` () = - let res = - try A.C.Helper.Add5(let mutable x = 2 in let mutable y = 3 in x + y) - with _ -> 1 - equal 10 res - -[] -let ``for .. downto works`` () = // See #411 - let mutable x = "" - for i = 1 to 5 do - x <- x + (string i) - equal "12345" x - let mutable y = "" - for i = 5 downto 1 do - y <- y + (string i) - equal "54321" y - -// [] -// let ``Self references in constructors work`` () = // See #124 -// let t = Test(5) -// equal 12 t.Value -// equal 17 t.Value2 +module ModuleBindings = + let inc1 (x: byref) = + x <- x + 1 + let inc2 x = + x + 1 + let modx = 3 + let mutable mody = 4 -// [] -// let ``Using self identifier from class definition in members works`` () = // See #124 -// let t = Test(5) -// t.Foo() |> equal 24 +open ModuleBindings [] -let ``Module members from partial functions work`` () = // See #115 - logItem1 "item1" |> equal "a = item1, b = item1" - -[] -let ``Class members from partial functions work`` () = // See #115 - let x = PartialFunctions() - x.logItem1 "item1" |> equal "a = item1, b = item1" +let ``Passing byref works`` () = + let mutable x = 5 + inc1 &x + let y = inc2 x + x |> equal 6 + y |> equal 7 [] -let ``Local values from partial functions work`` () = // See #115 - let logItem1 = log2 "item1" - let logItem2 = log2 "item2" - logItem1 "item1" |> equal "a = item1, b = item1" +let ``Module let bindings work`` () = + mody <- mody + 1 + let z = modx + mody + z |> equal 8 [] -let ``Custom computation expressions work`` () = - execMaybe 5 |> equal (Some 23) - execMaybe 99 |> equal None +let ``Units of measure work`` () = + let a = 4 + let b = 2 + let c = a / b + c |> equal (2) [] -let ``Units of measure work`` () = +let ``Units of measure work II`` () = 3 + 2 |> equal 5 - - let v1 = { x = 4.3; y = 5.; z = 2.8 } - let v2 = { x = 5.6; y = 3.8; z = 0. } - let v3 = v1 + v2 - equal 8.8 v3.y + // let v1 = { x = 4.3; y = 5.; z = 2.8 } + // let v2 = { x = 5.6; y = 3.8; z = 0. } + // let v3 = v1 + v2 + // equal 8.8 v3.y [] let ``Units of measure work with longs`` () = 3L + 2L |> equal 5L -[] -let ``Units of measure work with decimals`` () = - 3M + 2M |> equal 5M +// [] +// let ``Units of measure work with decimals`` () = +// 3M + 2M |> equal 5M [] -let ``Abbreviated measures work`` () = // #2313 +let ``Abbreviated units of measure work`` () = let x = 5. let c = MeasureTest() c.Method(5.) |> equal x [] -let ``Can resolve trait calls from another file with units of measure`` () = // See #2880 - let expected = MiscTestsHelper.Vector2 (8.209999999999999, 7.72) - let x = MiscTestsHelper.Vector2(4.5, 4.5) - MiscTestsHelper.Vector2(5.3, 4.6) |> lerp x 0.7 |> equal expected - -// [] -// let ``FSharp.UMX works`` () = -// let lookupById (orders : Order list) (id : string) = -// orders |> List.tryFind (fun o -> o.id = id) -// let order = -// { -// id = % "orderId" -// customer = % "customerId" -// quantity = % 42 -// } -// // lookupById [] order.customer // compiler error -// let orders = [{ order with quantity = %50 }] -// lookupById orders order.id -// |> Option.map (fun o -> UMX.untag o.quantity) -// |> equal (Some 50) - -// [] -// let ``FSharp.UMX: reflection info`` () = -// let fields = Reflection.FSharpType.GetRecordFields typeof -// fields.Length |> equal 3 - -// [] -// let ``Static constructors work`` () = -// let point1 = PointWithCounter(10, 52) -// sprintf "%d %d %d %d" (point1.Prop1) (point1.Prop2) (point1.CreatedCount) (point1.FunctionValue) -// |> equal "10 52 1 204" -// let point2 = PointWithCounter(20, 99) -// sprintf "%d %d %d %d" (point2.Prop1) (point2.Prop2) (point2.CreatedCount) (point2.FunctionValue) -// |> equal "20 99 2 598" - -// [] -// let ``File with single type in namespace compiles`` () = -// equal SingleTypeInNamespace.Hello "Hello" - -// [] -// let ``Type abbreviation in namespace compiles`` () = // See #140 -// let h = Util2.H(5) -// equal "5" h.Value +let ``Functions in union fields are uncurried`` () = + let res = UncurryUnion (-) |> applyUncurryUnion 5 2 + res |> equal 3 [] -let ``Multiple namespaces in same file work`` () = // See #1218 - A.C.Helper.Add5(9) |> equal 14 +let ``Functions in class fields are uncurried`` () = + let adder = TestClass((+)) + let res = adder.Add(2, 3) + res |> equal 5 [] -let ``Inline methods work`` () = - f 2 3 |> equal 5 +let ``automatically generated generic names don't conflict`` () = + MyRecord.Stringify 456 + |> equal 456 -[] -let ``Inline methods with this argument work`` () = // See #638 - let x = FooModule.FooInline() - x.Foo |> equal "FooBar" - x.Foofy 4 |> equal "BarBarBarBar" +#if FABLE_COMPILER_RUST +// open Fable.Core +open Fable.Core.Rust -[] -let ``Inline properties work`` () = - let x = FooModule.FooInline() - x.PropertyInline <- 3uy - x.PropertyInline |> equal 3uy +[] +type StructC = { x: int; y: int } +[] [] -let ``Inline extension methods with this argument work`` () = // See #638 - let x = FooModule.FooInline() - x.Bar2 |> equal "BarBar" - x.FoofyPlus 3 |> equal "BarBarBarBarBarBar" +let ``Simple outer attribute works`` (): unit = + failwith "Some error" +[] [] -let ``Inline extension methods in other files can be found`` () = // See #1667 - "HOLA CARACOLA".StartsWith("hola") |> equal false - "HOLA CARACOLA".StartsWithIgnoreCase("hola") |> equal true +let ``Name value outer attribute works`` (): unit = + failwith "Some error" +[] [] -let ``Inline overloaded methods work`` () = - let res1 = Type.New(5).Method(false).Method(true).Method() - let res2 = Type.New(5).MethodI(false).MethodI(true).MethodI() - equal res1.a res2.a - counter() |> equal 3 +let ``Delimited outer attribute works`` (): unit = + failwith "Some error" -[] -let ``Inline overloaded methods in other files work`` () = - let res1 = MiscTestsHelper.Type.New(5).Method(false).Method(true).Method() - let res2 = MiscTestsHelper.Type.New(5).MethodI(false).MethodI(true).MethodI() - equal res1.a res2.a - MiscTestsHelper.counter() |> equal 3 +// [] +// let f_async () = 2 -[] -let ``Inlined arguments with delayed resolution are only evaluated once`` () = - let mutable x = 0 - let foo() = - x <- x + 1 - 10 - let f = sum (foo()) - f 20 |> f |> equal 40 - equal 1 x +[] +let f_const () = 3 -// [] -// let ``Don't inline values that evaluate multiple times`` () = -// let li = [1;2;3;4] -// let res = List.map (FooModule.add FooModule.genericValue) li -// equal 1 FooModule.genericValueBackend -// equal [6;7;8;9] res - -[] -let ``Calls to core lib from a subfolder work`` () = - Util2.Helper.Format("{0} + {0} = {1}", 2, 4) - |> equal "2 + 2 = 4" - -[] -let ``Conversion to delegate works`` () = - (System.Func<_,_,_,_> f1).Invoke(1,2,3) |> equal 6 +[] +let f_unsafe () = 4 - let f = f1 - (System.Func<_,_,_,_> f).Invoke(1,2,3) |> equal 6 +[] +let f_extern () = 5 - let del = System.Func<_,_,_,_>(fun x y z -> x + y + z) - del.Invoke(1,2,3) |> equal 6 +// [] +// let await x = nativeOnly - (System.Func<_,_> f2).Invoke(2) |> equal 4 +// [] +// let ``Async attribute works`` (): unit = +// f_async () |> await |> equal 2 - // See #2400 - let func1 : Func = Func(fun () -> 8) - func1.Invoke() |> equal 8 +let ``Const attribute works`` (): unit = + f_const () |> equal 3 - let fn2 () = 9 - let func2 : Func = Func(fn2) - func2.Invoke() |> equal 9 +[] +let ``Unsafe attribute works`` (): unit = + f_unsafe () |> equal 4 - let func2b = Func(fn2) - func2b.Invoke() |> equal 9 - - let fn2c () () = 9 - let func2c : Func = Func(fn2c()) - func2c.Invoke() |> equal 9 - - let fn3 i = i + 4 - let func3 = Func(fn3) - func3.Invoke(7) |> equal 11 - - let fn4 x y = x * y - 3 - let func4 = Func(fn4) - func4.Invoke(4, 6) |> equal 21 - -[] -let ``Conversion to Func<_> works`` () = - (System.Func<_> f3).Invoke() |> equal 5 - let f = Func<_>(fun () -> 6) - f.Invoke() |> equal 6 +let ``Extern attribute works`` (): unit = + f_extern () |> equal 5 -[] -let ``Conversion to aliased Func<_> works`` () = - (MyDelegate f3).Invoke() |> equal 5 - let f = MyDelegate(fun () -> 6) - f.Invoke() |> equal 6 - -// TODO -// [] -// let ``Conversion to FSharpFunc<_,_,_> works`` () = -// let f x y = x + y -// let f = FSharpFunc<_,_,_>.Adapt(f) -// f.Invoke(1, 2) |> equal 3 - -// [] -// let ``Conversion to FSharpFunc<_,_,_,_> works`` () = -// let f x y z = x + y + z -// let f = FSharpFunc<_,_,_,_>.Adapt(f) -// f.Invoke(1, 2, 3) |> equal 6 - -[] -let ``Conversion to Action<_> works`` () = - let f1' = Action(fun i -> myMutableField <- i * 2) - let f2' = Action(f4) - let f3' = Action<_>(f6 4) - f1'.Invoke(1) - equal 2 myMutableField - f2'.Invoke(8) - equal 8 myMutableField - f3'.Invoke(10) - equal 40 myMutableField - -// TODO!!! -// [] -// let ``Conversion to Action works`` () = -// let f4' = Action(fun () -> myMutableField <- 7) -// let f5' = Action(f5) -// let f6' = Action(f7 3) -// let f7' i () = myMutableField <- i * 3 -// let f8' = Action(f7' 3) -// f4'.Invoke() -// equal 7 myMutableField -// f5'.Invoke() -// equal 5 myMutableField -// f6'.Invoke() -// equal 9 myMutableField -// f8'.Invoke() -// equal 9 myMutableField - -[] -let ``Multiple active pattern calls work`` () = - match " Hello ", " Bye " with - | NonEmpty "Hello", NonEmpty "Bye" -> true - | _ -> false - |> equal true - -// [] -// let ``ParamArray in object expression works`` () = -// let o = { new IFoo with member x.Bar(s: string, [] rest: obj[]) = String.Format(s, rest) } -// o.Bar("{0} + {0} = {1}", 2, 4) -// |> equal "2 + 2 = 4" - -// [] -// let ``Object expression can reference enclosing type and self`` () = // See #158 -// let f = Foo(5) -// let f2 = f.MakeFoo2() -// f2.Value <- 2 -// f.Value |> equal 12 -// f2.Test(2) |> equal 22 - -// [] -// let ``Nested object expressions work`` () = // See #158 -// let f = Foo(5) -// let f2 = f.MakeFoo2() -// f2.MakeFoo().Bar("Numbers") |> equal "Numbers: 10 20 5" - -// [] -// let ``References to enclosing type from object expression work`` () = // See #438 -// MyComponent("TestA").works1().doWork() |> equal "TestA-1" -// MyComponent("TestB").works2().doWork() |> equal "TestB-2" -// MyComponent("TestC").works3().doWork() |> equal "TestC-3" -// MyComponent("TestD").works4().doWork() |> equal "TestD-4" -// MyComponent("TestE").works5().doWork() |> equal "TestE-5" - -// [] -// let ``Properties in object expression work`` () = -// let mutable backend = 0 -// let o = { new IFoo3 with member x.Bar with get() = backend and set(v) = backend <- v } -// o.Bar |> equal 0 -// backend <- 5 -// o.Bar |> equal 5 -// o.Bar <- 10 -// o.Bar |> equal 10 - -// [] -// let ``Object expression from class works`` () = -// let o = { new SomeClass("World") with member x.ToString() = sprintf "Hello %s" x.Name } -// // TODO: Type testing for object expressions? -// // match box o with -// // | :? SomeClass as c -> c.ToString() -// // | _ -> "Unknown" -// // |> equal "Hello World" -// o.ToString() |> equal "Hello World" - -// [] -// let ``Inlined object expression doesn't change argument this context`` () = // See #1291 -// let t = TestClass(42) -// t.GetNum() |> equal 46 - -// [] -// let ``Object expressions don't optimize members away`` () = // See #1434 -// let o = { -// new Taster with -// member _.Starter = 5.5 -// member this.Taste(quality, quantity) = -// taste this quality quantity -// interface Eater with -// member _.Bite() = 25 -// } -// o.Taste(4., 6.) |> equal 28 - -// [] -// let ``Members are accessible in abstract class constructor inherited by object expr`` () = // See #2139 -// let x = ref 5 -// let obj = { new ObjectExprBase(x) with -// override _.dup x = x * x } -// equal 25 x.contents - -// [] -// let ``Composition with recursive `this` works`` () = -// let mutable x = 0 -// RecursiveType(fun f -> x <- f()) |> ignore -// equal 11 x - -// [] -// let ``Type extension static methods work`` () = -// let disposed = ref false -// let disp = IDisposable.Create(fun () -> disposed := true) -// disp.Dispose () -// equal true !disposed - -// [] -// let ``Type extension properties work`` () = -// let c = SomeClass("John") -// equal "John Smith" c.FullName - -// [] -// let ``Type extension methods work`` () = -// let c = SomeClass("John") -// c.NameTimes(1,2) |> equal "JohnJohnJohn" - -// [] -// let ``Type extension methods with same name work`` () = -// let c = AnotherClass(3) -// equal "3" c.FullName - -// [] -// let ``Type extension overloads work`` () = -// let c = AnotherClass(3) -// c.Overload("3") |> equal "33" -// c.Overload(3) |> equal 12 - -// [] -// let ``Extending different types with same name and same method works`` () = -// AnotherClass(5).Value2 |> equal 10 -// NestedModule.AnotherClass(5).Value2 |> equal 40 - -[] -let ``Module, members and properties with same name don't clash`` () = - StyleBuilderHelper.test() |> equal true - -[] -let ``Module mutable properties work`` () = - equal 10 Mutable.prop - Mutable.prop <- 5 - equal 5 Mutable.prop - -[] -let ``Accessing members of parent module with same name works`` () = - equal 5 Same.Same.shouldEqual5 - -[] -let ``Accessing members of child module with same name works`` () = - equal 10 Same.Same.shouldEqual10 - -[] -let ``Accessing members with same name as module works`` () = - equal 20 Same.Same.shouldEqual20 - -[] -let ``Naming values with same name as module works`` () = - equal 30 Same.Same.shouldEqual30 - -[] -let ``Can access nested recursive function with mangled name`` () = - Util.Bar.nestedRecursive 3 |> equal 10 - -[] -let ``Can access non nested recursive function with mangled name`` () = - Util.nonNestedRecursive "ja" |> equal "jajaja" - -[] -let ``Module members don't conflict with JS names`` () = - Util.Int32Array |> Array.sum |> equal 3 - -[] -let ``Modules don't conflict with JS names`` () = - Util.Float64Array.Float64Array |> Array.sum |> equal 7. - -// Modules and TestFixtures bind values in a slightly different way -// Test both cases -// [] -// let ``Binding doesn't shadow top-level values`` () = // See #130 -// equal 10 Util.B.c -// equal 20 Util.B.D.d - -// [] -// let ``Binding doesn't shadow top-level values (TestFixture)`` () = // See #130 -// equal 10 B.c -// equal 20 B.D.d - -// [] -// let ``Binding doesn't shadow top-level functions`` () = // See #130 -// equal 4 Util.B.d -// equal 0 Util.B.D.e - -// [] -// let ``Binding doesn't shadow top-level functions (TestFixture)`` () = // See #130 -// equal 4 B.d -// equal 0 B.D.e - -// [] -// let ``Setting a top-level value doesn't alter values at same level`` () = // See #130 -// equal 15 Util.a -// equal 25 Util.B.a - -// [] -// let ``Setting a top-level value doesn't alter values at same level (TestFixture)`` () = // See #130 -// equal 15 a -// equal 25 B.a - -[] -let ``Internal members can be accessed from other modules`` () = // See #163 - Internal.add 3 2 |> equal 5 - -[] -let ``Internal types can be accessed from other modules`` () = // See #163 - Internal.MyType.Subtract 3 2 |> equal 1 - -// In F# both branches of if-then-else has the same type, -// but this is not always true in Fable AST. For example when -// one branch is a Throw expression, it has always type Unit. -// So we should test that the type of the whole expression is not determined -// by the throw expression in one of its branches. -// -// The same applies to try-with expression. - -// #if FABLE_COMPILER -// // This test fails if the system language is set to other language than English -// [] -// let ``Pattern-matching against discriminated unions gives proper error message`` () = -// try -// let unitCircle = Circle 1 -// match unitCircle with -// | Rectangle(n, m) -> failwith "Should not happen" -// | Square n -> failwith "Should not happen" -// with -// | ex -> ex.Message.StartsWith "Match failure" |> equal true -// #endif - -[] -let ``Type of if-then-else expression is correctly determined when 'then' branch throws`` () = - let f () = - if false then failwith "error" else 7 - f () |> equal 7 - -[] -let ``Type of if-then-else expression is correctly determined when 'else' branch throws`` () = - let f () = - if true then 7 else failwith "error" - f () |> equal 7 - -[] -let ``Type of try-with expression is correctly determined when 'try' block throws`` () = - doesntThrow (fun () -> - let f () = - try failwith "error" with | _ -> 7 - f () |> equal 7 - ) - -[] -let ``Type of try-with expression is correctly determined when exception handler throws`` () = - doesntThrow (fun () -> - let f () = - try 7 with | _ -> failwith "error" - f () |> equal 7 - ) -[] -let ``use doesn't return on finally clause`` () = // See #211 - let foo() = - use c = new DisposableFoo() - c.Foo() - foo() |> equal 5 - -// [] -// let ``use calls Dispose at the end of the scope`` () = -// let cell = ref 0 -// let res = -// use c = new DisposableBar(cell) -// !cell -// res |> equal 10 -// !cell |> equal 20 - -// [] -// let ``use calls Dispose (of an object expression) at the end of the scope`` () = -// let cell = ref 0 -// let res = -// use c = createCellDiposable cell -// !cell -// res |> equal 10 -// !cell |> equal 20 - -// [] -// let ``Can use `use` with null`` () = // See #2719 -// let mutable x = 0 -// let msg = -// use __ = null : IDisposable -// "hooray" -// msg |> equal "hooray" -// x |> equal 0 - -// let msg = -// use __ = { new IDisposable with member _.Dispose() = x <- 1 } -// "booh" -// msg |> equal "booh" -// x |> equal 1 - -[] -let ``Unchecked.defaultof works`` () = - Unchecked.defaultof |> equal 0 - Unchecked.defaultof |> equal 0L - Unchecked.defaultof |> equal 0I - Unchecked.defaultof |> equal 0M - Unchecked.defaultof |> equal DateTime.MinValue - Unchecked.defaultof |> equal (TimeSpan.FromMilliseconds 0.) - Unchecked.defaultof |> equal false - Unchecked.defaultof |> equal null - Unchecked.defaultof |> equal Guid.Empty - let x = ValueType() - Unchecked.defaultof |> equal x - x.X |> equal 0 - -[] -let ``Unchecked.defaultof works with tuples`` () = // See #2491 - // TODO: Non-struct tuples - // let y: (int*int) = Unchecked.defaultof<_> - // equal null (box y) - let x: struct (int*int) = Unchecked.defaultof<_> - equal (struct (0, 0)) x - -[] -let ``Pattern matching optimization works (switch statement)`` () = - let mutable x = "" - let i = 4 - match i with - | 1 -> x <- "1" - | 2 -> x <- "2" - | 3 | 4 -> x <- "3" // Multiple cases are allowed - // | 5 | 6 as j -> x <- string j // This prevents the optimization - | 4 -> x <- "4" // Unreachable cases are removed - | _ -> x <- "?" - equal "3" x - - match "Bye" with - | "Hi" -> x <- "Bye" - | "Bye" -> let h = "Hi" in x <- sprintf "%s there!" h - | _ -> x <- "?" - equal "Hi there!" x - - // Pattern matching with Int64/UInt64 is not converted to switch - match 2L with - | 1L -> x <- "1L" - | 2L -> x <- "2L" - | _ -> x <- "?" - equal "2L" x - - // Pattern matching with boolean is not converted to switch - match false with - | true -> x <- "True" - | false -> x <- "False" - equal "False" x - - match MyEnum.One with - | MyEnum.One -> x <- "One" - | MyEnum.Two -> x <- "Two" - | _ -> failwith "never" - equal "One" x - -[] -let ``Pattern matching optimization works (switch expression)`` () = - let i = 4 - match i with - | 1 -> "1" - | 2 -> "2" - | 3 | 4 -> "3" - | _ -> "?" - |> equal "3" - - match "Bye" with - | "Hi" -> "Bye" - | "Bye" -> let h = "Hi" in sprintf "%s there!" h - | _ -> "?" - |> equal "Hi there!" - - match MyEnum.One with - | MyEnum.One -> "One" - | MyEnum.Two -> "Two" - | _ -> failwith "never" - |> equal "One" - -[] -let ``Pattern matching with same result for last pattern and wildcard works`` () = // #2357 - let x = 10 - let y = - match x with - | 1 - | 2 - | 3 - | 4 - | _ -> - sideEffect() - 5 - equal 5 y - -[] -let ``FSharpRef can be used in properties`` () = // See #521 - let r = ref false - let x = TestRef r - r := true - match x with TestRef r2 -> !r2 - |> equal true - -// [] -// let ``Recursive values work`` () = // See #237 -// mutableValue |> equal 5 -// recursive1() -// mutableValue |> equal 10 - -// [] -// let ``Module generic methods without arguments work`` () = -// let li = empty -// Seq.length li |> equal 1 - -[] -let ``Public members of private modules can be accessed`` () = // See #696 - MyPrivateModule.publicFoo() |> equal "foo bar" - -[] -let ``Public types of private modules can be accessed`` () = // See #841 - let thing = MyPrivateModule.Concrete() :> IInterface - thing.Member "World" "Hello" |> equal "Hello World" - -// [] -// let ``Types declared in signature file work`` () = // See #754 -// let t = Spaces.TRec.Create("haha", "hoho") -// t.Value |> equal "hahahoho" - -// [] -// let ``Primary constructor of types from signature files work`` () = // See #571 -// Spaces.Test(true).Status |> equal true -// Spaces.Test(false).Status |> equal false - -// [] -// let ``Two types with same name in different folders work`` () = // See #781 -// tempet.SayA.hello "Albert" -// |> equal "Hello Albert from SayA" -// tempet.SayB.hello "Albert" -// |> equal "Hello Albert from SayB" - -[] -let ``While with isNone doesn't hang with Some ()`` () = - Trampoline.run (fun _ -> Trampoline.Break "hello") () |> ignore - Trampoline.run (fun _ -> Trampoline.Break 42) () |> ignore - Trampoline.run (fun _ -> Trampoline.Break ()) () |> ignore - -[] -let ``Removing optional arguments not in tail position works`` () = - Internal.MyType.Add(y=6) |> equal 26 - -[] -let ``Inlined methods can have optional arguments`` () = - StaticClass.Add(2, 3) |> equal 5 - StaticClass.Add(5) |> equal 7 - -// [] -// let ``DefaultParameterValue works`` () = -// StaticClass.DefaultParam() |> equal true - -// [] -// let ``DefaultParameterValue works with null`` () = -// StaticClass.DefaultNullParam() |> isNull |> equal true -// StaticClass.DefaultNullParam(5) |> isNull |> equal false - -[] -let ``Ignore shouldn't return value`` () = // See #1360 - let producer () = 7 - equal (box ()) (box(ignore(producer()))) - -// [] -// let ``Can import files specified via globbing patterns`` () = // See #1942 -// Glob.hello "Albert" -// |> equal "Hello Albert from Glob" - -[] -let ``Mutable variables can be passed by reference`` () = - let a = 1 - let mutable b = 2 - incByRef a &b - b |> equal 3 - addInRef a &b |> equal 4 - b |> equal 3 - setOutRef a &b - b |> equal 1 - -// [] -// let ``Public mutable variables can be passed by reference`` () = -// let a = 1 -// mutX <- 2 -// incByRef a &mutX -// mutX |> equal 3 -// addInRef a &mutX |> equal 4 -// mutX |> equal 3 -// setOutRef a &mutX -// mutX |> equal 1 - -[] -let ``Mutable fields can be passed by reference`` () = - let a = 1 - let foo: MutableFoo = { x = 2 } - incByRef a &foo.x - foo.x |> equal 3 - addInRef a &foo.x |> equal 4 - foo.x |> equal 3 - setOutRef a &foo.x - foo.x |> equal 1 - -[] -let ``Assigning to unit works`` () = // See #2548 - let mutable value = 2 - let inline doit x (f: 'A -> 'T) = - let mutable r = Unchecked.defaultof<_> - r <- f x - r - - doit "a" (fun a -> a + "b") - |> equal "ab" - - doit 2 (fun x -> value <- value + x) - value |> equal 4 - -[] -let ``Mutating variables is not postponed (functions)`` () = - let ``inlineData PR #2683`` = [3, 2, 5; 5, 10, 15; 10, 20, 30] - - let runCase (l: int) (r: int) (expect: int) = - let mutable left = 0 - let call() = - left <- l - r - - let run() = - let right = call() - left + right - - run() |> equal expect - - for (l, r, ``l + r``) in ``inlineData PR #2683`` do - runCase l r ``l + r`` - -// [] -// let ``Mutating variables is not postponed (classes)`` () = -// let ``inlineData PR #2683`` = [3, 2, 5; 5, 10, 15; 10, 20, 30] - -// let runCase (l: int) (r: int) (expect: int) = -// InliningMutationTest(l, r).Run() |> equal expect -// InliningMutationTest(l, r).Run() |> equal expect - -// for (l, r, ``l + r``) in ``inlineData PR #2683`` do -// runCase l r ``l + r`` - -// [] -// let ``References captured by >> are eagerly evaluated`` () = // See #2851 -// let times2 x = x * 2 -// fn <- fn >> times2 -// fn 5 |> equal 10 +#endif //FABLE_COMPILER_RUST