diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 98e51366088..03f891e6870 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -7552,8 +7552,26 @@ 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 + + // 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 = + bindsRemaining + |> 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 4c746116db1..cb9a6840981 100644 --- a/tests/fsharp/core/letrec/test.fsx +++ b/tests/fsharp/core/letrec/test.fsx @@ -643,6 +643,170 @@ 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 + +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 + + +//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