From d49b30cb9b832555327ec6b63623f9d4b4b1b286 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 15 Nov 2021 14:59:38 +0000 Subject: [PATCH 1/4] fix non-nested mutrec bindings --- src/fsharp/IlxGen.fs | 8 ++++++-- tests/fsharp/core/letrec/test.fsx | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 98e51366088..af7724e5c34 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -7552,8 +7552,12 @@ and GenModuleDef cenv (cgbuf: CodeGenBuffer) qname lazyInitInfo eenv x = GenExnDef cenv cgbuf.mgbuf eenvinner m tc else GenTypeDef cenv cgbuf.mgbuf lazyInitInfo eenvinner m tc - for mbind in mbinds do - GenModuleBinding cenv cgbuf qname lazyInitInfo eenvinner m mbind + if mbinds |> List.forall (function ModuleOrNamespaceBinding.Binding _ -> true | _ -> false) then + let recBinds = mbinds |> List.choose (function ModuleOrNamespaceBinding.Binding recBind -> Some recBind | _ -> None) + GenLetRecBindings cenv cgbuf eenv (recBinds, m) + else + for mbind in mbinds do + GenModuleBinding cenv cgbuf qname lazyInitInfo eenvinner m mbind eenvinner | TMDefLet(bind, _) -> diff --git a/tests/fsharp/core/letrec/test.fsx b/tests/fsharp/core/letrec/test.fsx index 4c746116db1..88302ddf372 100644 --- a/tests/fsharp/core/letrec/test.fsx +++ b/tests/fsharp/core/letrec/test.fsx @@ -643,6 +643,33 @@ module Test3 = test "vwekjwve95" (tag()) 2 test "vwekjwve96" (tag()) 3 +module Test12384 = + type Node = + { + Next: Node + Value: int + } + + let rec one = + { + Next = two + Value = 1 + } + + and two = + { + Next = one + Value = 2 + } + printfn "%A" one + printfn "%A" two + test "cweewlwne1" one.Value 1 + test "cweewlwne2" one.Next.Value 2 + test "cweewlwne3" one.Next.Next.Value 1 + test "cweewlwne4" two.Value 2 + test "cweewlwne5" two.Next.Value 1 + test "cweewlwne6" two.Next.Next.Value 2 + #if TESTS_AS_APP let RUN() = !failures #else From db18cce2c44336e788f3bf42913bf2b35a7bb78d Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 15 Nov 2021 15:15:51 +0000 Subject: [PATCH 2/4] fix non-nested mutrec bindings --- src/fsharp/IlxGen.fs | 22 ++++++++--- tests/fsharp/core/letrec/test.fsx | 62 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index af7724e5c34..00618370638 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -7552,12 +7552,24 @@ and GenModuleDef cenv (cgbuf: CodeGenBuffer) qname lazyInitInfo eenv x = GenExnDef cenv cgbuf.mgbuf eenvinner m tc else GenTypeDef cenv cgbuf.mgbuf lazyInitInfo eenvinner m tc - if mbinds |> List.forall (function ModuleOrNamespaceBinding.Binding _ -> true | _ -> false) then - let recBinds = mbinds |> List.choose (function ModuleOrNamespaceBinding.Binding recBind -> Some recBind | _ -> None) - GenLetRecBindings cenv cgbuf eenv (recBinds, m) - else - for mbind in mbinds do + + // Generate chunks of non-nested bindings together to allow recursive fixups. + let mutable bindsRemaining = mbinds + while not bindsRemaining.IsEmpty do + match bindsRemaining with + | ModuleOrNamespaceBinding.Binding _ :: _ -> + let recBinds = + bindsRemaining + |> List.takeWhile (function ModuleOrNamespaceBinding.Binding _ -> true | _ -> false) + |> List.map (function ModuleOrNamespaceBinding.Binding recBind -> recBind | _ -> failwith "unexpected") + let otherBinds = mbinds |> List.skipWhile (function ModuleOrNamespaceBinding.Binding _ -> true | _ -> false) + GenLetRecBindings cenv cgbuf eenv (recBinds, m) + bindsRemaining <- otherBinds + | (ModuleOrNamespaceBinding.Module _ as mbind) :: rest -> GenModuleBinding cenv cgbuf qname lazyInitInfo eenvinner m mbind + bindsRemaining <- rest + | [] -> failwith "unreachable" + eenvinner | TMDefLet(bind, _) -> diff --git a/tests/fsharp/core/letrec/test.fsx b/tests/fsharp/core/letrec/test.fsx index 88302ddf372..022f68c469e 100644 --- a/tests/fsharp/core/letrec/test.fsx +++ b/tests/fsharp/core/letrec/test.fsx @@ -670,6 +670,68 @@ module Test12384 = test "cweewlwne5" two.Next.Value 1 test "cweewlwne6" two.Next.Next.Value 2 +module Test12384b = + type Node = + { + Next: Node + Value: int + } + + let rec one = + { + Next = two + Value = 1 + } + + and two = + { + Next = one + Value = 2 + } + // Also test the case where the two recursive bindings occur with a nested module after + module M = + let f x = x + 1 + + printfn "%A" one + printfn "%A" two + test "cweewlwne1a" one.Value 1 + test "cweewlwne2a" one.Next.Value 2 + test "cweewlwne3a" one.Next.Next.Value 1 + test "cweewlwne4a" two.Value 2 + test "cweewlwne5a" two.Next.Value 1 + test "cweewlwne6a" two.Next.Next.Value 2 + +module rec Test12384c = + type Node = + { + Next: Node + Value: int + } + + let one = + { + Next = two + Value = 1 + } + + let two = + { + Next = one + Value = 2 + } + // Also test the case where the two recursive bindings occur with a nested module after + module M = + let f x = x + 1 + + printfn "%A" one + printfn "%A" two + test "cweewlwne1b" one.Value 1 + test "cweewlwne2b" one.Next.Value 2 + test "cweewlwne3b" one.Next.Next.Value 1 + test "cweewlwne4b" two.Value 2 + test "cweewlwne5b" two.Next.Value 1 + test "cweewlwne6b" two.Next.Next.Value 2 + #if TESTS_AS_APP let RUN() = !failures #else From a7e1d848a26d3e788a45db12448e029abc5bcc76 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 15 Nov 2021 15:16:36 +0000 Subject: [PATCH 3/4] fix non-nested mutrec bindings --- src/fsharp/IlxGen.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 00618370638..03f891e6870 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -7562,7 +7562,9 @@ and GenModuleDef cenv (cgbuf: CodeGenBuffer) qname lazyInitInfo eenv x = bindsRemaining |> List.takeWhile (function ModuleOrNamespaceBinding.Binding _ -> true | _ -> false) |> List.map (function ModuleOrNamespaceBinding.Binding recBind -> recBind | _ -> failwith "unexpected") - let otherBinds = mbinds |> List.skipWhile (function ModuleOrNamespaceBinding.Binding _ -> true | _ -> false) + let otherBinds = + bindsRemaining + |> List.skipWhile (function ModuleOrNamespaceBinding.Binding _ -> true | _ -> false) GenLetRecBindings cenv cgbuf eenv (recBinds, m) bindsRemaining <- otherBinds | (ModuleOrNamespaceBinding.Module _ as mbind) :: rest -> From 0105990c254bf95527d94a50da554d3c6d4bc175 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 15 Nov 2021 16:20:10 +0000 Subject: [PATCH 4/4] fix non-nested mutrec bindings --- tests/fsharp/core/letrec/test.fsx | 75 +++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/fsharp/core/letrec/test.fsx b/tests/fsharp/core/letrec/test.fsx index 022f68c469e..cb9a6840981 100644 --- a/tests/fsharp/core/letrec/test.fsx +++ b/tests/fsharp/core/letrec/test.fsx @@ -732,6 +732,81 @@ module rec Test12384c = test "cweewlwne5b" two.Next.Value 1 test "cweewlwne6b" two.Next.Next.Value 2 + +//Note, this case doesn't initialize successfully because of the intervening module. Tracked by #12384 + +(* +module rec Test12384d = + type Node = + { + Next: Node + Value: int + } + + let one = + { + Next = two + Value = 1 + } + + // An intervening module declaration + module M = + let x() = one + + let two = + { + Next = one + Value = 2 + } + + printfn "%A" one + printfn "%A" two + test "cweewlwne1b" one.Value 1 + test "cweewlwne2b" one.Next.Value 2 + test "cweewlwne3b" one.Next.Next.Value 1 + test "cweewlwne1b" (M.x()).Value 1 + test "cweewlwne2b" (M.x()).Next.Value 2 + test "cweewlwne3b" (M.x()).Next.Next.Value 1 + test "cweewlwne4b" two.Value 2 + test "cweewlwne5b" two.Next.Value 1 + test "cweewlwne6b" two.Next.Next.Value 2 +*) + +module rec Test12384e = + type Node = + { + Next: Node + Value: int + } + + let one = + { + Next = two + Value = 1 + } + + // An intervening type declaration + type M() = + static member X() = one + + let two = + { + Next = one + Value = 2 + } + + printfn "%A" one + printfn "%A" two + test "cweewlwne1b" one.Value 1 + test "cweewlwne2b" one.Next.Value 2 + test "cweewlwne3b" one.Next.Next.Value 1 + test "cweewlwne1b" (M.X()).Value 1 + test "cweewlwne2b" (M.X()).Next.Value 2 + test "cweewlwne3b" (M.X()).Next.Next.Value 1 + test "cweewlwne4b" two.Value 2 + test "cweewlwne5b" two.Next.Value 1 + test "cweewlwne6b" two.Next.Next.Value 2 + #if TESTS_AS_APP let RUN() = !failures #else