Created
July 2, 2018 16:21
-
-
Save dsyme/82c49d5cb63d04f3b2b3502d51e6277c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(************************************** | |
F# tests for validating tail calls | |
are correctly generated by the | |
compiler and taken by the .NET | |
runtime. | |
Tested and passes when ran against | |
F# v1.9.6.16 | |
Run this script via: | |
Chris Smith | |
chrsmith@microsoft.com | |
**************************************) | |
open System | |
// -------------------------------------------------- | |
// Utilities | |
// -------------------------------------------------- | |
// Check an expression against the result. If they do not | |
// match the script immediately terminates. | |
let RunTest msg actual expected = | |
if actual = expected then | |
Console.WriteLine(msg + ": YES") | |
else | |
Console.WriteLine("\n***** " + msg + ": FAIL\n") | |
exit 1 | |
let HugeInt = 10000000 | |
// -------------------------------------------------- | |
// Tail Call Tests | |
// -------------------------------------------------- | |
// Not a tail call - test that this overflows the stack | |
let rec nonTailCall x = | |
if x = 0 | |
then 0 | |
else 1 + nonTailCall (x - 1) | |
// Simple tail call, the compiler will convert to while loop | |
let rec simpleTail1 x acc = | |
if x = 0 | |
then acc | |
else simpleTail1 (x - 1) (acc + 1) | |
RunTest "simpleTail1" (simpleTail1 HugeInt 0) HugeInt | |
// Another simple example | |
let rec simpleTail2 acc = function | |
|0 -> acc | |
|i -> simpleTail2 (acc+1) (i-1) | |
RunTest "simpleTail2" (simpleTail2 HugeInt 0) HugeInt | |
// Mutually recurisve tail call, compiler will emit tail instruction | |
let rec mutualTail1IsOdd x = | |
match x with | |
| 1 -> true | |
| n -> mutualTail1IsEven (x - 1) | |
and mutualTail1IsEven x = | |
match x with | |
| 1 -> false | |
| 0 -> true | |
| n -> mutualTail1IsOdd (x - 1) | |
RunTest "mutualTail1IsOdd" (mutualTail1IsOdd HugeInt) false | |
RunTest "mutualTail1IsEven" (mutualTail1IsEven HugeInt) true | |
// Interesting parameter types (large stack frame) | |
// Making mutually recurisve to ensure the .tail call | |
let rec mutualTail2IsOdd a b c d e f g h i x = | |
match x with | |
| 1 -> (true, a, b, c) | |
| n -> mutualTail2IsEven a b c d e f g h i 0L 0L 0L 0L (x - 1) | |
and mutualTail2IsEven a b c d e f g h i j k l m x = | |
match x with | |
| 1 -> (false, a, b, c) | |
| 0 -> (true, a, b, c) | |
| n -> mutualTail2IsOdd a b c d e f g h i (x - 1) | |
RunTest "mutualTail2IsOdd" (mutualTail2IsOdd 0.0M [| 1 .. 10 |] "str" 0L 0L 0L 0L 0L 0L HugeInt) (false, 0.0M, [| 1 .. 10 |], "str") | |
RunTest "mutualTail2IsEven" (mutualTail2IsEven 0.0M [| 1 .. 10 |] "str" 0L 0L 0L 0L 0L 0L 0L 0L 0L 0L HugeInt) (true, 0.0M, [| 1 .. 10 |], "str") | |
// Generic tail call within a type | |
type TailCallLoop<'T1>() = | |
let rec f x = if x = 0 then 1 else f (x - 1) | |
let v5 = f 10000000 | |
member x.Result = | |
f 10000000 + v5 | |
RunTest "TailCallLoop<int>" ((new TailCallLoop<int>()).Result) 2 | |
RunTest "TailCallLoop<DateTime>" ((new TailCallLoop<DateTime>()).Result) 2 | |
// Generic tail call within a type | |
type TailCallLoopGenericClassAndMethod<'T1>(resultA: 'T1) = | |
member this.Method1<'T2>(x:int, resultB: 'T2) = | |
if x = 0 then (resultA, resultB) else this.Method2 (x - 1, resultB) | |
member this.Method2<'T2>(x:int, resultB: 'T2) = | |
if x = 0 then (resultA, resultB) else this.Method1 (x - 1, resultB) | |
RunTest "TailCallLoopGenericClassAndMethod<byte>.Method1<byte>" ((new TailCallLoopGenericClassAndMethod<byte>(3uy)).Method1<byte>(10000000, 4uy)) (3uy, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethod<byte>.Method1<int>" ((new TailCallLoopGenericClassAndMethod<byte>(3uy)).Method1<int>(10000000, 4)) (3uy, 4) | |
RunTest "TailCallLoopGenericClassAndMethod<byte>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethod<byte>(3uy)).Method1<DateTime>(10000000, DateTime.MinValue)) (3uy, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethod<byte>.Method1<string>" ((new TailCallLoopGenericClassAndMethod<byte>(3uy)).Method1<string>(10000000, "abc")) (3uy, "abc") | |
RunTest "TailCallLoopGenericClassAndMethod<int>.Method1<byte>" ((new TailCallLoopGenericClassAndMethod<int>(3)).Method1<byte>(10000000, 4uy)) (3, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethod<int>.Method1<int>" ((new TailCallLoopGenericClassAndMethod<int>(3)).Method1<int>(10000000, 4)) (3, 4) | |
RunTest "TailCallLoopGenericClassAndMethod<int>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethod<int>(3)).Method1<DateTime>(10000000, DateTime.MinValue)) (3, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethod<int>.Method1<string>" ((new TailCallLoopGenericClassAndMethod<int>(3)).Method1<string>(10000000, "abc")) (3, "abc") | |
RunTest "TailCallLoopGenericClassAndMethod<DateTime>.Method1<byte>" ((new TailCallLoopGenericClassAndMethod<DateTime>(DateTime.MaxValue)).Method1<byte>(10000000, 4uy)) (DateTime.MaxValue, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethod<DateTime>.Method1<int>" ((new TailCallLoopGenericClassAndMethod<DateTime>(DateTime.MaxValue)).Method1<int>(10000000, 4)) (DateTime.MaxValue, 4) | |
RunTest "TailCallLoopGenericClassAndMethod<DateTime>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethod<DateTime>(DateTime.MaxValue)).Method1<DateTime>(10000000, DateTime.MinValue)) (DateTime.MaxValue, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethod<DateTime>.Method1<string>" ((new TailCallLoopGenericClassAndMethod<DateTime>(DateTime.MaxValue)).Method1<string>(10000000, "abc")) (DateTime.MaxValue, "abc") | |
RunTest "TailCallLoopGenericClassAndMethod<string>.Method1<byte>" ((new TailCallLoopGenericClassAndMethod<string>("qq")).Method1<byte>(10000000, 4uy)) ("qq", 4uy) | |
RunTest "TailCallLoopGenericClassAndMethod<string>.Method1<int>" ((new TailCallLoopGenericClassAndMethod<string>("qq")).Method1<int>(10000000, 4)) ("qq", 4) | |
RunTest "TailCallLoopGenericClassAndMethod<string>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethod<string>("qq")).Method1<DateTime>(10000000, DateTime.MinValue)) ("qq", DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethod<string>.Method1<string>" ((new TailCallLoopGenericClassAndMethod<string>("qq")).Method1<string>(10000000, "abc")) ("qq", "abc") | |
// Generic tail call within a type via virtual method in abstract class hierarchy | |
[<AbstractClass>] | |
type AbstractTailCallLoopGenericClassAndMethod<'T1>(resultA: 'T1) = | |
abstract Method1<'T2> : int * 'T2 -> ('T1 * 'T2) | |
abstract Method2<'T2> : int * 'T2 -> ('T1 * 'T2) | |
default this.Method2<'T2>(x:int, resultB: 'T2) = | |
if x = 0 then (resultA, resultB) else this.Method1 (x - 1, resultB) | |
type TailCallLoopGenericClassAndMethodAbstractClass<'T1>(resultA: 'T1) = | |
inherit AbstractTailCallLoopGenericClassAndMethod<'T1>(resultA) | |
override this.Method1<'T2>(x:int, resultB: 'T2) = | |
if x = 0 then (resultA, resultB) else this.Method2 (x - 1, resultB) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<byte>" ((new TailCallLoopGenericClassAndMethodAbstractClass<byte>(3uy)).Method1<byte>(10000000, 4uy)) (3uy, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<int>" ((new TailCallLoopGenericClassAndMethodAbstractClass<byte>(3uy)).Method1<int>(10000000, 4)) (3uy, 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethodAbstractClass<byte>(3uy)).Method1<DateTime>(10000000, DateTime.MinValue)) (3uy, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<string>" ((new TailCallLoopGenericClassAndMethodAbstractClass<byte>(3uy)).Method1<string>(10000000, "abc")) (3uy, "abc") | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<byte>" ((new TailCallLoopGenericClassAndMethodAbstractClass<int>(3)).Method1<byte>(10000000, 4uy)) (3, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<int>" ((new TailCallLoopGenericClassAndMethodAbstractClass<int>(3)).Method1<int>(10000000, 4)) (3, 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethodAbstractClass<int>(3)).Method1<DateTime>(10000000, DateTime.MinValue)) (3, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<string>" ((new TailCallLoopGenericClassAndMethodAbstractClass<int>(3)).Method1<string>(10000000, "abc")) (3, "abc") | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<byte>" ((new TailCallLoopGenericClassAndMethodAbstractClass<DateTime>(DateTime.MaxValue)).Method1<byte>(10000000, 4uy)) (DateTime.MaxValue, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<int>" ((new TailCallLoopGenericClassAndMethodAbstractClass<DateTime>(DateTime.MaxValue)).Method1<int>(10000000, 4)) (DateTime.MaxValue, 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethodAbstractClass<DateTime>(DateTime.MaxValue)).Method1<DateTime>(10000000, DateTime.MinValue)) (DateTime.MaxValue, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<string>" ((new TailCallLoopGenericClassAndMethodAbstractClass<DateTime>(DateTime.MaxValue)).Method1<string>(10000000, "abc")) (DateTime.MaxValue, "abc") | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<string>.Method1<byte>" ((new TailCallLoopGenericClassAndMethodAbstractClass<string>("qq")).Method1<byte>(10000000, 4uy)) ("qq", 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<string>.Method1<int>" ((new TailCallLoopGenericClassAndMethodAbstractClass<string>("qq")).Method1<int>(10000000, 4)) ("qq", 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<string>.Method1<DateTime>" ((new TailCallLoopGenericClassAndMethodAbstractClass<string>("qq")).Method1<DateTime>(10000000, DateTime.MinValue)) ("qq", DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DatstringeTime>.Method1<string>" ((new TailCallLoopGenericClassAndMethodAbstractClass<string>("qq")).Method1<string>(10000000, "abc")) ("qq", "abc") | |
// Generic tail call within a type via interface | |
type InterfaceTailCallLoopGenericInterface<'T1> = | |
interface | |
abstract Method1<'T2> : int * 'T2 -> ('T1 * 'T2) | |
abstract Method2<'T2> : int * 'T2 -> ('T1 * 'T2) | |
end | |
type TailCallLoopImplementGenericInterface<'T1>(resultA: 'T1) = | |
interface InterfaceTailCallLoopGenericInterface<'T1> with | |
member this.Method1<'T2>(x:int, resultB: 'T2) = | |
if x = 0 then (resultA, resultB) else (this :> InterfaceTailCallLoopGenericInterface<'T1>).Method2 (x - 1, resultB) | |
member this.Method2<'T2>(x:int, resultB: 'T2) = | |
if x = 0 then (resultA, resultB) else (this :> InterfaceTailCallLoopGenericInterface<'T1>).Method1 (x - 1, resultB) | |
let create<'T> result = TailCallLoopImplementGenericInterface<'T>(result) :> InterfaceTailCallLoopGenericInterface<'T> | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<byte>" ((create<byte>(3uy)).Method1<byte>(10000000, 4uy)) (3uy, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<int>" ((create<byte>(3uy)).Method1<int>(10000000, 4)) (3uy, 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<DateTime>" ((create<byte>(3uy)).Method1<DateTime>(10000000, DateTime.MinValue)) (3uy, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<byte>.Method1<string>" ((create<byte>(3uy)).Method1<string>(10000000, "abc")) (3uy, "abc") | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<byte>" ((create<int>(3)).Method1<byte>(10000000, 4uy)) (3, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<int>" ((create<int>(3)).Method1<int>(10000000, 4)) (3, 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<DateTime>" ((create<int>(3)).Method1<DateTime>(10000000, DateTime.MinValue)) (3, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<int>.Method1<string>" ((create<int>(3)).Method1<string>(10000000, "abc")) (3, "abc") | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<byte>" ((create<DateTime>(DateTime.MaxValue)).Method1<byte>(10000000, 4uy)) (DateTime.MaxValue, 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<int>" ((create<DateTime>(DateTime.MaxValue)).Method1<int>(10000000, 4)) (DateTime.MaxValue, 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<DateTime>" ((create<DateTime>(DateTime.MaxValue)).Method1<DateTime>(10000000, DateTime.MinValue)) (DateTime.MaxValue, DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DateTime>.Method1<string>" ((create<DateTime>(DateTime.MaxValue)).Method1<string>(10000000, "abc")) (DateTime.MaxValue, "abc") | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<string>.Method1<byte>" ((create<string>("qq")).Method1<byte>(10000000, 4uy)) ("qq", 4uy) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<string>.Method1<int>" ((create<string>("qq")).Method1<int>(10000000, 4)) ("qq", 4) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<string>.Method1<DateTime>" ((create<string>("qq")).Method1<DateTime>(10000000, DateTime.MinValue)) ("qq", DateTime.MinValue) | |
RunTest "TailCallLoopGenericClassAndMethodAbstractClass<DatstringeTime>.Method1<string>" ((create<string>("qq")).Method1<string>(10000000, "abc")) ("qq", "abc") | |
// Generic tail call within a type | |
type TailCallLoopGenericClass<'T1>(resultA: 'T1) = | |
member this.Method1(x:int) = | |
if x = 0 then resultA else this.Method2 (x - 1) | |
member this.Method2(x:int) = | |
if x = 0 then resultA else this.Method1 (x - 1) | |
member this.Result = | |
this.Method1 10000000 | |
RunTest "TailCallLoopGenericClass<int>" ((new TailCallLoopGenericClass<int>(3)).Result) 3 | |
RunTest "TailCallLoopGenericClass<DateTime>" ((new TailCallLoopGenericClass<DateTime>(DateTime.MinValue)).Result) DateTime.MinValue | |
RunTest "TailCallLoopGenericClass<string>" ((new TailCallLoopGenericClass<string>("abc")).Result) "abc" | |
// Generic tail call calling sizeof within a type | |
type StaticTailCallLoop<'T1>() = | |
static let rec f x = if x = 0 then sizeof<'T1> else f (x - 1) | |
static let v5 = f HugeInt | |
static member Result = | |
f HugeInt + v5 | |
RunTest "StaticTailCallLoop<int>" (StaticTailCallLoop<int>.Result) 8 | |
RunTest "StaticTailCallLoop<DateTime>" (StaticTailCallLoop<DateTime>.Result) 16 | |
// -------------------------------------------------- | |
// Some regression tests for F# related to tail calls | |
// -------------------------------------------------- | |
do RunTest "Seq.filter-length1" ({ 1 .. 1000000 } |> Seq.filter (fun n -> n <> 1) |> Seq.length) 999999;; | |
do RunTest "Seq.filter-length2" ({ 1 .. 1000000 } |> Seq.filter (fun n -> n = 1) |> Seq.length) 1;; | |
do RunTest "Seq.filter-length3" ({ 1 .. 1000000 } |> Seq.filter (fun n -> n % 2 = 0) |> Seq.length) 500000;; | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment