diff --git a/BenchmarkDotNet.Artifacts/results/TaskPerf.Benchmarks-report-github.md b/BenchmarkDotNet.Artifacts/results/TaskPerf.Benchmarks-report-github.md
new file mode 100644
index 00000000000..6cce7d6b240
--- /dev/null
+++ b/BenchmarkDotNet.Artifacts/results/TaskPerf.Benchmarks-report-github.md
@@ -0,0 +1,67 @@
+``` ini
+
+BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
+Intel Xeon CPU E5-1620 0 3.60GHz, 1 CPU, 8 logical and 4 physical cores
+.NET Core SDK=5.0.104
+ [Host] : .NET Core 5.0.6 (CoreCLR 5.0.621.22011, CoreFX 5.0.621.22011), X64 RyuJIT DEBUG
+ DefaultJob : .NET Core 5.0.6 (CoreCLR 5.0.621.22011, CoreFX 5.0.621.22011), X64 RyuJIT
+
+
+```
+| Method | Categories | Mean | Error | StdDev | Median | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
+|-------------------------------------------- |----------------------- |---------------:|--------------:|--------------:|---------------:|-------:|--------:|-----------:|--------:|------:|------------:|
+| ManyWriteFile_CSharpTasks | ManyWriteFile | 4,186.6 μs | 81.32 μs | 201.00 μs | 4,187.4 μs | 1.00 | 0.00 | 15.6250 | - | - | 117288 B |
+| ManyWriteFile_taskBuilder | ManyWriteFile | 5,744.5 μs | 124.34 μs | 356.76 μs | 5,712.3 μs | 1.38 | 0.11 | 62.5000 | - | - | 444882 B |
+| ManyWriteFile_async | ManyWriteFile | 6,108.8 μs | 121.27 μs | 271.23 μs | 6,132.1 μs | 1.46 | 0.10 | 132.8125 | - | - | 704991 B |
+| ManyWriteFile_task | ManyWriteFile | 5,014.3 μs | 100.18 μs | 204.65 μs | 4,973.0 μs | 1.19 | 0.07 | 15.6250 | - | - | 116996 B |
+| ManyWriteFile_async2 | ManyWriteFile | 5,358.4 μs | 106.05 μs | 211.79 μs | 5,374.1 μs | 1.27 | 0.07 | 15.6250 | - | - | 117140 B |
+| | | | | | | | | | | | |
+| NonAsyncBinds_CSharpTasks | NonAsyncBinds | 17,444.8 μs | 374.32 μs | 1,097.80 μs | 17,030.4 μs | 1.00 | 0.00 | 15125.0000 | - | - | 79200000 B |
+| NonAsyncBinds_taskBuilder | NonAsyncBinds | 26,000.1 μs | 440.68 μs | 880.09 μs | 25,621.3 μs | 1.47 | 0.09 | 22187.5000 | - | - | 116000000 B |
+| NonAsyncBinds_async | NonAsyncBinds | 1,303,920.2 μs | 63,453.67 μs | 177,930.90 μs | 1,269,891.9 μs | 74.52 | 10.59 | 52000.0000 | - | - | 276000000 B |
+| NonAsyncBinds_async2 | NonAsyncBinds | 24,213.0 μs | 191.23 μs | 178.88 μs | 24,168.6 μs | 1.38 | 0.09 | 18812.5000 | 62.5000 | - | 98400000 B |
+| NonAsyncBinds_task | NonAsyncBinds | 16,693.5 μs | 302.84 μs | 759.75 μs | 16,586.1 μs | 0.95 | 0.07 | 15125.0000 | - | - | 79200000 B |
+| | | | | | | | | | | | |
+| AsyncBinds_CSharpTasks | AsyncBinds | 8,335.4 μs | 190.47 μs | 558.61 μs | 8,197.5 μs | 1.00 | 0.00 | 15.6250 | - | - | 112119 B |
+| AsyncBinds_taskBuilder | AsyncBinds | 11,567.6 μs | 229.28 μs | 522.18 μs | 11,360.0 μs | 1.39 | 0.12 | 296.8750 | - | - | 1559252 B |
+| AsyncBinds_async | AsyncBinds | 127,872.2 μs | 3,090.27 μs | 8,866.56 μs | 127,900.8 μs | 15.38 | 1.47 | 1333.3333 | - | - | 8312000 B |
+| AsyncBinds_task | AsyncBinds | 9,897.9 μs | 314.55 μs | 927.46 μs | 10,058.4 μs | 1.19 | 0.15 | 31.2500 | - | - | 192096 B |
+| AsyncBinds_async2 | AsyncBinds | 8,165.2 μs | 156.64 μs | 347.09 μs | 8,051.9 μs | 0.98 | 0.07 | 62.5000 | - | - | 352218 B |
+| | | | | | | | | | | | |
+| SingleSyncTask_CSharpTasks | SingleSyncTask | 8,668.5 μs | 170.65 μs | 233.59 μs | 8,595.4 μs | 1.00 | 0.00 | - | - | - | - |
+| SingleSyncTask_taskBuilder | SingleSyncTask | 12,402.4 μs | 103.89 μs | 92.10 μs | 12,366.7 μs | 1.43 | 0.04 | 9171.8750 | - | - | 48000000 B |
+| SingleSyncTask_async | SingleSyncTask | 3,659,569.1 μs | 109,062.88 μs | 298,557.86 μs | 3,576,228.4 μs | 409.11 | 38.68 | 91000.0000 | - | - | 475999216 B |
+| SingleSyncTask_task | SingleSyncTask | 10,642.1 μs | 90.05 μs | 84.23 μs | 10,622.2 μs | 1.23 | 0.04 | - | - | - | - |
+| SingleSyncTask_async2 | SingleSyncTask | 28,177.5 μs | 263.90 μs | 220.37 μs | 28,134.6 μs | 3.25 | 0.08 | 7625.0000 | - | - | 40000000 B |
+| | | | | | | | | | | | |
+| SyncBuilderLoop_NormalCode | sync | 81,206.3 μs | 1,598.95 μs | 1,570.38 μs | 80,813.7 μs | 1.00 | 0.00 | 36714.2857 | - | - | 192176000 B |
+| SyncBuilderLoop_WorkflowCode | sync | 81,811.3 μs | 1,610.38 μs | 3,140.93 μs | 80,621.9 μs | 1.02 | 0.05 | 36714.2857 | - | - | 192176000 B |
+| | | | | | | | | | | | |
+| TinyVariableSizedList_Builtin | TinyVariableSizedList | 57,762.3 μs | 1,721.40 μs | 4,827.00 μs | 56,313.5 μs | 1.00 | 0.00 | 20375.0000 | - | - | 106666656 B |
+| TinyVariableSizedList_NewBuilder | TinyVariableSizedList | 17,122.1 μs | 341.76 μs | 650.23 μs | 17,233.0 μs | 0.29 | 0.03 | 2031.2500 | - | - | 10666656 B |
+| | | | | | | | | | | | |
+| VariableSizedList_Builtin | VariableSizedList | 330,273.8 μs | 6,534.98 μs | 13,051.05 μs | 328,659.3 μs | 1.00 | 0.00 | 63000.0000 | - | - | 330666624 B |
+| VariableSizedList_NewBuilder | VariableSizedList | 167,451.6 μs | 2,840.89 μs | 2,217.98 μs | 167,326.7 μs | 0.51 | 0.02 | 44750.0000 | - | - | 234666934 B |
+| | | | | | | | | | | | |
+| FixedSizeList_Builtin | FixedSizedList | 100,128.5 μs | 1,968.29 μs | 3,885.21 μs | 100,084.8 μs | 1.00 | 0.00 | 61166.6667 | - | - | 320000000 B |
+| FixedSizeList_NewBuilder | FixedSizedList | 229,639.0 μs | 4,589.37 μs | 11,846.63 μs | 227,278.1 μs | 2.30 | 0.13 | 61000.0000 | - | - | 320000000 B |
+| | | | | | | | | | | | |
+| TinyVariableSizedArray_Builtin | TinyVariableSizedArray | 100,414.3 μs | 1,995.07 μs | 4,462.26 μs | 100,631.3 μs | 1.00 | 0.00 | 30000.0000 | - | - | 157333304 B |
+| TinyVariableSizedArray_NewBuilder | TinyVariableSizedArray | 28,538.5 μs | 632.86 μs | 1,825.93 μs | 28,426.7 μs | 0.29 | 0.02 | 10687.5000 | - | - | 55999968 B |
+| | | | | | | | | | | | |
+| VariableSizedArray_Builtin | VariableSizedArray | 356,489.5 μs | 3,061.89 μs | 2,714.29 μs | 356,174.5 μs | 1.00 | 0.00 | 77000.0000 | - | - | 405333840 B |
+| VariableSizedArray_NewBuilder | VariableSizedArray | 161,909.3 μs | 861.02 μs | 672.23 μs | 161,860.8 μs | 0.45 | 0.00 | 59000.0000 | - | - | 309333476 B |
+| | | | | | | | | | | | |
+| FixedSizeArray_Builtin | FixedSizedArray | 32,944.8 μs | 777.85 μs | 2,293.52 μs | 32,391.8 μs | 1.00 | 0.00 | 19875.0000 | - | - | 104000000 B |
+| FixedSizeArray_NewBuilder | FixedSizedArray | 219,352.6 μs | 4,288.40 μs | 10,837.34 μs | 217,830.4 μs | 6.65 | 0.60 | 82333.3333 | - | - | 432000000 B |
+| | | | | | | | | | | | |
+| MultiStepOption_OldBuilder | MultiStepOption | 63,360.5 μs | 1,199.04 μs | 1,062.92 μs | 63,133.5 μs | 1.00 | 0.00 | 38750.0000 | - | - | 202666703 B |
+| MultiStepOption_NewBuilder | MultiStepOption | 20,179.8 μs | 622.44 μs | 1,775.86 μs | 19,705.5 μs | 0.29 | 0.02 | 13437.5000 | - | - | 70399968 B |
+| MultiStepOption_NoBuilder | MultiStepOption | 19,727.8 μs | 469.72 μs | 1,362.75 μs | 19,395.3 μs | 0.32 | 0.02 | 13437.5000 | - | - | 70399968 B |
+| | | | | | | | | | | | |
+| MultiStepValueOption_OldBuilder | MultiStepValueOption | 47,237.3 μs | 909.93 μs | 759.83 μs | 47,211.0 μs | 1.00 | 0.00 | 19090.9091 | - | - | 100266664 B |
+| MultiStepValueOption_NewBuilder | MultiStepValueOption | 4,144.6 μs | 46.31 μs | 43.32 μs | 4,146.1 μs | 0.09 | 0.00 | - | - | - | - |
+| MultiStepValueOption_NoBuilder | MultiStepValueOption | 3,824.0 μs | 75.26 μs | 73.92 μs | 3,806.3 μs | 0.08 | 0.00 | - | - | - | - |
+| | | | | | | | | | | | |
+| NestedForLoops_taskSeqUsingRawResumableCode | taskSeq | 983.7 μs | 18.23 μs | 17.90 μs | 984.7 μs | 1.61 | 0.04 | 54.6875 | - | - | 295641 B |
+| NestedForLoops_CSharpAsyncEnumerable | taskSeq | 612.9 μs | 10.04 μs | 8.90 μs | 615.5 μs | 1.00 | 0.00 | 24.4141 | - | - | 131280 B |
diff --git a/FSharp.sln b/FSharp.sln
index bcbb76848ae..2b07028feb2 100644
--- a/FSharp.sln
+++ b/FSharp.sln
@@ -60,17 +60,21 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Service.Tes
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Service", "src\fsharp\FSharp.Compiler.Service\FSharp.Compiler.Service.fsproj", "{9B4CF83C-C215-4EA0-9F8B-B5A77090F634}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FSharp.Compiler.Service.Tests support", "FSharp.Compiler.Service.Tests support", "{452EED3C-AA87-471F-B9AC-0F4479C5820C}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{CE70D631-C5DC-417E-9CDA-B16097BEF1AC}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp_Analysis", "tests\service\data\CSharp_Analysis\CSharp_Analysis.csproj", "{F8743670-C8D4-41B3-86BE-BBB1226C352F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroPerfCSharp", "tests\benchmarks\MicroPerf\CS\MicroPerfCSharp.csproj", "{348DCC13-DD3E-4214-B040-5A74E8C6B782}"
EndProject
-Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TestTP", "tests\service\data\TestTP\TestTP.fsproj", "{7BFA159A-BF9D-4489-BF46-1B83ACCEEE0F}"
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MicroPerf", "tests\benchmarks\MicroPerf\MicroPerf.fsproj", "{9735B522-37F7-478C-A0C6-6C60BCC53390}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TaskPerfCSharp", "tests\benchmarks\TaskPerfCSharp\TaskPerfCSharp.csproj", "{CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{E02ADBCA-D6C0-4898-A8AA-86DE6EBE2DC2}"
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TaskPerf", "tests\benchmarks\TaskPerf\TaskPerf.fsproj", "{51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroPerfCSharp", "tests\benchmarks\MicroPerf\CS\MicroPerfCSharp.csproj", "{2904313F-7782-4522-894C-DC946DFFE22D}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FSharp.Compiler.Service.Tests support", "FSharp.Compiler.Service.Tests support", "{452EED3C-AA87-471F-B9AC-0F4479C5820C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp_Analysis", "tests\service\data\CSharp_Analysis\CSharp_Analysis.csproj", "{F8743670-C8D4-41B3-86BE-BBB1226C352F}"
EndProject
-Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MicroPerf", "tests\benchmarks\MicroPerf\MicroPerf.fsproj", "{C888A81D-2372-4B4A-8BA1-525AEE3918D6}"
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TestTP", "tests\service\data\TestTP\TestTP.fsproj", "{7BFA159A-BF9D-4489-BF46-1B83ACCEEE0F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -310,6 +314,54 @@ Global
{9B4CF83C-C215-4EA0-9F8B-B5A77090F634}.Release|Any CPU.Build.0 = Release|Any CPU
{9B4CF83C-C215-4EA0-9F8B-B5A77090F634}.Release|x86.ActiveCfg = Release|Any CPU
{9B4CF83C-C215-4EA0-9F8B-B5A77090F634}.Release|x86.Build.0 = Release|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Debug|x86.Build.0 = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Proto|x86.Build.0 = Debug|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Release|Any CPU.Build.0 = Release|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Release|x86.ActiveCfg = Release|Any CPU
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782}.Release|x86.Build.0 = Release|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Debug|x86.Build.0 = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Proto|x86.Build.0 = Debug|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Release|x86.ActiveCfg = Release|Any CPU
+ {9735B522-37F7-478C-A0C6-6C60BCC53390}.Release|x86.Build.0 = Release|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Debug|x86.Build.0 = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Proto|x86.Build.0 = Debug|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Release|x86.ActiveCfg = Release|Any CPU
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB}.Release|x86.Build.0 = Release|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Debug|x86.Build.0 = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Proto|x86.Build.0 = Debug|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Release|x86.ActiveCfg = Release|Any CPU
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4}.Release|x86.Build.0 = Release|Any CPU
{F8743670-C8D4-41B3-86BE-BBB1226C352F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8743670-C8D4-41B3-86BE-BBB1226C352F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8743670-C8D4-41B3-86BE-BBB1226C352F}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -334,35 +386,12 @@ Global
{7BFA159A-BF9D-4489-BF46-1B83ACCEEE0F}.Release|Any CPU.Build.0 = Release|Any CPU
{7BFA159A-BF9D-4489-BF46-1B83ACCEEE0F}.Release|x86.ActiveCfg = Release|Any CPU
{7BFA159A-BF9D-4489-BF46-1B83ACCEEE0F}.Release|x86.Build.0 = Release|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Debug|x86.Build.0 = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Proto|Any CPU.Build.0 = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Proto|x86.ActiveCfg = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Proto|x86.Build.0 = Debug|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Release|Any CPU.Build.0 = Release|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Release|x86.ActiveCfg = Release|Any CPU
- {2904313F-7782-4522-894C-DC946DFFE22D}.Release|x86.Build.0 = Release|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Debug|x86.Build.0 = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Proto|Any CPU.Build.0 = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Proto|x86.ActiveCfg = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Proto|x86.Build.0 = Debug|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Release|Any CPU.Build.0 = Release|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Release|x86.ActiveCfg = Release|Any CPU
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
+ {D5870CF0-ED51-4CBC-B3D7-6F56DA84AC06} = {B8DDA694-7939-42E3-95E5-265C2217C142}
{DED3BBD7-53F4-428A-8C9F-27968E768605} = {3058BC79-8E79-4645-B05D-48CC182FA8A6}
{702A7979-BCF9-4C41-853E-3ADFC9897890} = {B8DDA694-7939-42E3-95E5-265C2217C142}
{C94C257C-3C0A-4858-B5D8-D746498D1F08} = {3881429D-A97A-49EB-B7AE-A82BA5FE9C77}
@@ -384,11 +413,13 @@ Global
{25568CD2-E654-4C8F-BE5B-59BABFC5BD20} = {07482B5E-4980-4285-B732-820F15870284}
{DDFD06DC-D7F2-417F-9177-107764EEBCD8} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
{9B4CF83C-C215-4EA0-9F8B-B5A77090F634} = {3881429D-A97A-49EB-B7AE-A82BA5FE9C77}
+ {348DCC13-DD3E-4214-B040-5A74E8C6B782} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC}
+ {9735B522-37F7-478C-A0C6-6C60BCC53390} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC}
+ {CF9F3F98-7BFB-4945-A4A5-668DF0AC65AB} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC}
+ {51B569A8-17C5-4EBD-8AAC-240E0B3AD8C4} = {CE70D631-C5DC-417E-9CDA-B16097BEF1AC}
{452EED3C-AA87-471F-B9AC-0F4479C5820C} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
{F8743670-C8D4-41B3-86BE-BBB1226C352F} = {452EED3C-AA87-471F-B9AC-0F4479C5820C}
{7BFA159A-BF9D-4489-BF46-1B83ACCEEE0F} = {452EED3C-AA87-471F-B9AC-0F4479C5820C}
- {2904313F-7782-4522-894C-DC946DFFE22D} = {E02ADBCA-D6C0-4898-A8AA-86DE6EBE2DC2}
- {C888A81D-2372-4B4A-8BA1-525AEE3918D6} = {E02ADBCA-D6C0-4898-A8AA-86DE6EBE2DC2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BD5177C7-1380-40E7-94D2-7768E1A8B1B8}
diff --git a/FSharpBuild.Directory.Build.props b/FSharpBuild.Directory.Build.props
index 767e0da6ebc..bcdbffa1c12 100644
--- a/FSharpBuild.Directory.Build.props
+++ b/FSharpBuild.Directory.Build.props
@@ -87,4 +87,13 @@
$(ProtoOutputPath)\fsc\Microsoft.FSharp.Overrides.NetSdk.targets
+
+
+
diff --git a/VisualFSharp.sln b/VisualFSharp.sln
index d35c1ba10b9..a51b52cc044 100644
--- a/VisualFSharp.sln
+++ b/VisualFSharp.sln
@@ -152,6 +152,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibraryProject", "vsintegra
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TutorialProject", "vsintegration\ProjectTemplates\TutorialProject\TutorialProject.csproj", "{2937CBEC-262D-4C94-BE1D-291FAB72E3E8}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TaskPerfCSharp", "tests\benchmarks\TaskPerfCSharp\TaskPerfCSharp.csproj", "{D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}"
+EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "TaskPerf", "tests\benchmarks\TaskPerf\TaskPerf.fsproj", "{03596D51-754D-4644-8E23-84EC9532ABDC}"
+EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.ComponentTests", "tests\FSharp.Compiler.ComponentTests\FSharp.Compiler.ComponentTests.fsproj", "{0610FB97-7C15-422A-86FD-32335C6DF14D}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Service", "src\fsharp\FSharp.Compiler.Service\FSharp.Compiler.Service.fsproj", "{B5A9BBD9-2F45-4722-A6CA-BAE3C64CD4E2}"
@@ -904,6 +908,30 @@ Global
{2937CBEC-262D-4C94-BE1D-291FAB72E3E8}.Release|Any CPU.Build.0 = Release|Any CPU
{2937CBEC-262D-4C94-BE1D-291FAB72E3E8}.Release|x86.ActiveCfg = Release|Any CPU
{2937CBEC-262D-4C94-BE1D-291FAB72E3E8}.Release|x86.Build.0 = Release|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Debug|x86.Build.0 = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Proto|x86.Build.0 = Debug|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Release|x86.ActiveCfg = Release|Any CPU
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0}.Release|x86.Build.0 = Release|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Debug|x86.Build.0 = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Proto|Any CPU.ActiveCfg = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Proto|Any CPU.Build.0 = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Proto|x86.ActiveCfg = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Proto|x86.Build.0 = Debug|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Release|x86.ActiveCfg = Release|Any CPU
+ {03596D51-754D-4644-8E23-84EC9532ABDC}.Release|x86.Build.0 = Release|Any CPU
{0610FB97-7C15-422A-86FD-32335C6DF14D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0610FB97-7C15-422A-86FD-32335C6DF14D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0610FB97-7C15-422A-86FD-32335C6DF14D}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -1059,6 +1087,8 @@ Global
{44155269-9B30-43DA-B97F-4F36F887B211} = {12EF27FD-A34B-4373-860A-F9FCE9651859}
{B53D9D05-8EF7-43A6-9A5B-0B113CBC54F8} = {12EF27FD-A34B-4373-860A-F9FCE9651859}
{2937CBEC-262D-4C94-BE1D-291FAB72E3E8} = {12EF27FD-A34B-4373-860A-F9FCE9651859}
+ {D5ECF8DF-E150-4AE3-B613-AB2B0FFA93E0} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
+ {03596D51-754D-4644-8E23-84EC9532ABDC} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
{0610FB97-7C15-422A-86FD-32335C6DF14D} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
{B5A9BBD9-2F45-4722-A6CA-BAE3C64CD4E2} = {3881429D-A97A-49EB-B7AE-A82BA5FE9C77}
{14F3D3D6-5C8E-43C2-98A2-17EA704D4DEA} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449}
diff --git a/src/fsharp/CheckComputationExpressions.fs b/src/fsharp/CheckComputationExpressions.fs
index fc3d9ace548..df0d2e4c278 100644
--- a/src/fsharp/CheckComputationExpressions.fs
+++ b/src/fsharp/CheckComputationExpressions.fs
@@ -857,7 +857,7 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
// 2. incompatible types: int and string
// with SynExpr.ArbitraryAfterError we have only first one
let wrapInArbErrSequence l caption =
- SynExpr.Sequential (DebugPointAtSequential.Both, true, l, (arbExpr(caption, l.Range.EndRange)), l.Range)
+ SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, true, l, (arbExpr(caption, l.Range.EndRange)), l.Range)
let mkOverallExprGivenVarSpaceExpr, varSpaceInner =
let isNullableOp opId =
@@ -929,7 +929,7 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
| SynExpr.ForEach (spForLoop, SeqExprOnly _seqExprOnly, isFromSource, pat, sourceExpr, innerComp, _) ->
let wrappedSourceExpr = mkSourceExprConditional isFromSource sourceExpr
- let mFor = match spForLoop with DebugPointAtFor.Yes m -> m | _ -> pat.Range
+ let mFor = match spForLoop with DebugPointAtFor.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.For) | _ -> pat.Range
let mPat = pat.Range
let spBind = match spForLoop with DebugPointAtFor.Yes m -> DebugPointAtBinding.Yes m | DebugPointAtFor.No -> DebugPointAtBinding.NoneAtSticky
if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mFor ad "For" builderTy) then
@@ -947,13 +947,13 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
translatedCtxt (mkSynCall "For" mFor [wrappedSourceExpr; SynExpr.MatchLambda (false, sourceExpr.Range, [SynMatchClause(pat, None, holeFill, mPat, DebugPointForTarget.Yes)], spBind, mFor) ])) )
| SynExpr.For (spBind, id, start, dir, finish, innerComp, m) ->
- let mFor = match spBind with DebugPointAtFor.Yes m -> m | _ -> m
+ let mFor = match spBind with DebugPointAtFor.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.For) | _ -> m
if isQuery then errorR(Error(FSComp.SR.tcNoIntegerForLoopInQuery(), mFor))
Some (trans CompExprTranslationPass.Initial q varSpace (elimFastIntegerForLoop (spBind, id, start, dir, finish, innerComp, m)) translatedCtxt )
| SynExpr.While (spWhile, guardExpr, innerComp, _) ->
let mGuard = guardExpr.Range
- let mWhile = match spWhile with DebugPointAtWhile.Yes m -> m | _ -> mGuard
+ let mWhile = match spWhile with DebugPointAtWhile.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.While) | _ -> mGuard
if isQuery then error(Error(FSComp.SR.tcNoWhileInQuery(), mWhile))
if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mWhile ad "While" builderTy) then
error(Error(FSComp.SR.tcRequireBuilderMethod("While"), mWhile))
@@ -963,7 +963,7 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
| SynExpr.TryFinally (innerComp, unwindExpr, mTryToLast, spTry, _spFinally) ->
- let mTry = match spTry with DebugPointAtTry.Yes m -> m | _ -> mTryToLast
+ let mTry = match spTry with DebugPointAtTry.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.Try) | _ -> mTryToLast
if isQuery then error(Error(FSComp.SR.tcNoTryFinallyInQuery(), mTry))
if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env mTry ad "TryFinally" builderTy) then
error(Error(FSComp.SR.tcRequireBuilderMethod("TryFinally"), mTry))
@@ -1048,9 +1048,10 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
| SynExpr.DoBang (rhsExpr, m) ->
let sp =
match sp with
- | DebugPointAtSequential.ExprOnly -> DebugPointAtBinding.Yes m
- | DebugPointAtSequential.StmtOnly -> DebugPointAtBinding.NoneAtDo
- | DebugPointAtSequential.Both -> DebugPointAtBinding.Yes m
+ | DebugPointAtSequential.SuppressExpr -> DebugPointAtBinding.NoneAtDo
+ | DebugPointAtSequential.SuppressBoth -> DebugPointAtBinding.NoneAtDo
+ | DebugPointAtSequential.SuppressStmt -> DebugPointAtBinding.Yes m
+ | DebugPointAtSequential.SuppressNeither -> DebugPointAtBinding.Yes m
Some(trans CompExprTranslationPass.Initial q varSpace (SynExpr.LetOrUseBang (sp, false, true, SynPat.Const(SynConst.Unit, rhsExpr.Range), rhsExpr, [], innerComp2, m)) translatedCtxt)
// "expr; cexpr" is treated as sequential execution
@@ -1298,7 +1299,7 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
Some(translatedCtxt (mkSynCall "Bind" mMatch [matchExpr; consumeExpr]))
| SynExpr.TryWith (innerComp, _mTryToWith, clauses, _mWithToLast, mTryToLast, spTry, _spWith) ->
- let mTry = match spTry with DebugPointAtTry.Yes m -> m | _ -> mTryToLast
+ let mTry = match spTry with DebugPointAtTry.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.Try) | _ -> mTryToLast
if isQuery then error(Error(FSComp.SR.tcTryWithMayNotBeUsedInQueries(), mTry))
let clauses = clauses |> List.map (fun (SynMatchClause(pat, cond, clauseComp, patm, sp)) -> SynMatchClause(pat, cond, transNoQueryOps clauseComp, patm, sp))
@@ -1456,7 +1457,7 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
| None ->
// This only occurs in final position in a sequence
match comp with
- // "do! expr;" in final position is treated as { let! () = expr in return () } when Return is provided or as { let! () = expr in zero } otherwise
+ // "do! expr;" in final position is treated as { let! () = expr in return () } when Return is provided (and no Zero with Default attribute is available) or as { let! () = expr in zero } otherwise
| SynExpr.DoBang (rhsExpr, m) ->
let mUnit = rhsExpr.Range
let rhsExpr = mkSourceExpr rhsExpr
@@ -1465,7 +1466,9 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Return" builderTy) then
SynExpr.ImplicitZero m
else
- SynExpr.YieldOrReturn((false, true), SynExpr.Const(SynConst.Unit, m), m)
+ match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy with
+ | minfo :: _ when MethInfoHasAttribute cenv.g m cenv.g.attrib_DefaultValueAttribute minfo -> SynExpr.ImplicitZero m
+ | _ -> SynExpr.YieldOrReturn ((false, true), SynExpr.Const (SynConst.Unit, m), m)
trans CompExprTranslationPass.Initial q varSpace (SynExpr.LetOrUseBang (DebugPointAtBinding.NoneAtDo, false, false, SynPat.Const(SynConst.Unit, mUnit), rhsExpr, [], bodyExpr, m)) translatedCtxt
// "expr;" in final position is treated as { expr; zero }
@@ -1483,9 +1486,9 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
let fillExpr =
if enableImplicitYield then
let implicitYieldExpr = mkSynCall "Yield" comp.Range [comp]
- SynExpr.SequentialOrImplicitYield(DebugPointAtSequential.ExprOnly, comp, holeFill, implicitYieldExpr, comp.Range)
+ SynExpr.SequentialOrImplicitYield(DebugPointAtSequential.SuppressExpr, comp, holeFill, implicitYieldExpr, comp.Range)
else
- SynExpr.Sequential(DebugPointAtSequential.ExprOnly, true, comp, holeFill, comp.Range)
+ SynExpr.Sequential(DebugPointAtSequential.SuppressExpr, true, comp, holeFill, comp.Range)
translatedCtxt fillExpr)
and transBind q varSpace bindRange bindName bindArgs (consumePat: SynPat) spBind (innerComp: SynExpr) translatedCtxt =
@@ -1688,7 +1691,7 @@ let mkSeqFinally (cenv: cenv) env m genTy e1 e2 =
mkCallSeqFinally cenv.g m genResultTy e1 e2
let mkSeqExprMatchClauses (pat', vspecs) innerExpr =
- [TClause(pat', None, TTarget(vspecs, innerExpr, DebugPointForTarget.Yes), pat'.Range) ]
+ [TClause(pat', None, TTarget(vspecs, innerExpr, DebugPointForTarget.Yes, None), pat'.Range) ]
let compileSeqExprMatchClauses (cenv: cenv) env inputExprMark (pat: Pattern, vspecs) innerExpr inputExprOpt bindPatTy genInnerTy =
let patMark = pat.Range
@@ -1733,7 +1736,7 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp overallTy m =
// We attach the debug point to the lambda expression so we can fetch it out again in LowerComputedListOrArraySeqExpr
let mFor =
match spFor with
- | DebugPointAtFor.Yes m -> m
+ | DebugPointAtFor.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.For)
| _ -> enumExprMark
match pat', vspecs, innerExpr with
@@ -1771,7 +1774,7 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp overallTy m =
// We attach the debug point to the lambda expression so we can fetch it out again in LowerComputedListOrArraySeqExpr
let mWhile =
match spWhile with
- | DebugPointAtWhile.Yes m -> m
+ | DebugPointAtWhile.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.While)
| _ -> guardExprMark
let innerExpr = mkDelayedExpr mWhile innerExpr
@@ -1784,12 +1787,12 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp overallTy m =
// We attach the debug points to the lambda expressions so we can fetch it out again in LowerComputedListOrArraySeqExpr
let mTry =
match spTry with
- | DebugPointAtTry.Yes m -> m
+ | DebugPointAtTry.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.Try)
| _ -> unwindExpr.Range
let mFinally =
match spFinally with
- | DebugPointAtFinally.Yes m -> m
+ | DebugPointAtFinally.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.Finally)
| _ -> unwindExpr.Range
let innerExpr = mkDelayedExpr mTry innerExpr
@@ -1847,7 +1850,7 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp overallTy m =
let innerExpr, tpenv = tcSequenceExprBody envinner genOuterTy tpenv innerComp
let mBind =
match spBind with
- | DebugPointAtBinding.Yes m -> m
+ | DebugPointAtBinding.Yes m -> m.NoteDebugPoint(RangeDebugPointKind.Binding)
| _ -> inputExpr.Range
let inputExprMark = inputExpr.Range
let matchv, matchExpr = compileSeqExprMatchClauses cenv env inputExprMark (pat', vspecs) innerExpr (Some inputExpr) bindPatTy genOuterTy
@@ -1865,7 +1868,7 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp overallTy m =
(fun tpenv (SynMatchClause(pat, cond, innerComp, _, sp)) ->
let pat', cond', vspecs, envinner, tpenv = TcMatchPattern cenv matchty env tpenv (pat, cond)
let innerExpr, tpenv = tcSequenceExprBody envinner genOuterTy tpenv innerComp
- TClause(pat', cond', TTarget(vspecs, innerExpr, sp), pat'.Range), tpenv)
+ TClause(pat', cond', TTarget(vspecs, innerExpr, sp, None), pat'.Range), tpenv)
tpenv
clauses
let inputExprTy = tyOfExpr cenv.g inputExpr
@@ -1901,7 +1904,7 @@ let TcSequenceExpression (cenv: cenv) env tpenv comp overallTy m =
expr, tpenv
| Choice2Of2 stmt ->
let m = comp.Range
- let resExpr = Expr.Sequential(stmt, mkSeqEmpty cenv env m genOuterTy, NormalSeq, DebugPointAtSequential.ExprOnly, m)
+ let resExpr = Expr.Sequential(stmt, mkSeqEmpty cenv env m genOuterTy, NormalSeq, DebugPointAtSequential.SuppressExpr, m)
resExpr, tpenv
and tcSequenceExprBodyAsSequenceOrStatement env genOuterTy tpenv comp =
diff --git a/src/fsharp/CheckDeclarations.fs b/src/fsharp/CheckDeclarations.fs
index 8b47a983b8c..7bca4c36993 100644
--- a/src/fsharp/CheckDeclarations.fs
+++ b/src/fsharp/CheckDeclarations.fs
@@ -1299,7 +1299,7 @@ module IncrClassChecking =
(isPriorToSuperInit, (fun e ->
let e = match adjustSafeInitFieldExprOpt with None -> e | Some ae -> mkCompGenSequential m ae e
- mkSequential DebugPointAtSequential.Both m assignExpr e)), []
+ mkSequential DebugPointAtSequential.SuppressNeither m assignExpr e)), []
/// Work out the implicit construction side effects of a 'let', 'let rec' or 'do'
/// binding in the implicit class construction sequence
@@ -1327,7 +1327,7 @@ module IncrClassChecking =
| IncrClassDo (doExpr, isStatic) ->
let doExpr = reps.FixupIncrClassExprPhase2C cenv (Some thisVal) safeStaticInitInfo thisTyInst doExpr
- let binder = (fun e -> mkSequential DebugPointAtSequential.Both doExpr.Range doExpr e)
+ let binder = (fun e -> mkSequential DebugPointAtSequential.SuppressNeither doExpr.Range doExpr e)
let isPriorToSuperInit = false
if isStatic then
([(isPriorToSuperInit, binder)], [], []), reps
@@ -1347,7 +1347,7 @@ module IncrClassChecking =
| Some v ->
let setExpr = mkRefCellSet g m ctorInfo.InstanceCtorThisVal.Type (exprForVal m v) (exprForVal m ctorInfo.InstanceCtorThisVal)
let setExpr = reps.FixupIncrClassExprPhase2C cenv (Some thisVal) safeStaticInitInfo thisTyInst setExpr
- let binder = (fun e -> mkSequential DebugPointAtSequential.Both setExpr.Range setExpr e)
+ let binder = (fun e -> mkSequential DebugPointAtSequential.SuppressNeither setExpr.Range setExpr e)
let isPriorToSuperInit = false
yield (isPriorToSuperInit, binder) ]
@@ -1361,7 +1361,7 @@ module IncrClassChecking =
| SafeInitField (rfref, _) ->
let setExpr = mkRecdFieldSetViaExprAddr (exprForVal m thisVal, rfref, thisTyInst, mkOne g m, m)
let setExpr = reps.FixupIncrClassExprPhase2C cenv (Some thisVal) safeStaticInitInfo thisTyInst setExpr
- let binder = (fun e -> mkSequential DebugPointAtSequential.Both setExpr.Range setExpr e)
+ let binder = (fun e -> mkSequential DebugPointAtSequential.SuppressNeither setExpr.Range setExpr e)
let isPriorToSuperInit = false
yield (isPriorToSuperInit, binder)
| NoSafeInitInfo ->
@@ -1448,7 +1448,7 @@ module IncrClassChecking =
| _ ->
inheritsExpr
- let spAtSuperInit = (if inheritsIsVisible then DebugPointAtSequential.Both else DebugPointAtSequential.StmtOnly)
+ let spAtSuperInit = (if inheritsIsVisible then DebugPointAtSequential.SuppressNeither else DebugPointAtSequential.SuppressStmt)
mkSequential spAtSuperInit m inheritsExpr ctorBody
// Add the normal
@@ -5662,7 +5662,8 @@ let emptyTcEnv g =
eModuleOrNamespaceTypeAccumulator = ref (Construct.NewEmptyModuleOrNamespaceType Namespace)
eFamilyType = None
eCtorInfo = None
- eCallerMemberName = None }
+ eCallerMemberName = None
+ eLambdaArgInfos = [] }
let CreateInitialTcEnv(g, amap, scopem, assemblyName, ccus) =
(emptyTcEnv g, ccus) ||> List.fold (fun env (ccu, autoOpens, internalsVisible) ->
diff --git a/src/fsharp/CheckExpressions.fs b/src/fsharp/CheckExpressions.fs
index 3babb1945f1..21a9375b6a1 100644
--- a/src/fsharp/CheckExpressions.fs
+++ b/src/fsharp/CheckExpressions.fs
@@ -215,6 +215,9 @@ type TcEnv =
eCtorInfo: CtorInfo option
eCallerMemberName: string option
+
+ // Active arg infos in iterated lambdas , allowing us to determine the attributes of arguments
+ eLambdaArgInfos: ArgReprInfo list list
}
member tenv.DisplayEnv = tenv.eNameResEnv.DisplayEnv
@@ -1333,13 +1336,13 @@ let MakeAndPublishVal cenv env (altActualParent, inSig, declKind, vrec, vscheme,
let vis, _ = ComputeAccessAndCompPath env (Some declKind) id.idRange vis overrideVis actualParent
- let inlineFlag =
- if HasFSharpAttributeOpt cenv.g cenv.g.attrib_DllImportAttribute attrs then
- if inlineFlag = ValInline.PseudoVal || inlineFlag = ValInline.Always then
- errorR(Error(FSComp.SR.tcDllImportStubsCannotBeInlined(), m))
- ValInline.Never
- else
- let implflags =
+ let inlineFlag =
+ if HasFSharpAttributeOpt cenv.g cenv.g.attrib_DllImportAttribute attrs then
+ if inlineFlag = ValInline.Always then
+ errorR(Error(FSComp.SR.tcDllImportStubsCannotBeInlined(), m))
+ ValInline.Never
+ else
+ let implflags =
match TryFindFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute attrs with
| Some (Attrib(_, _, [ AttribInt32Arg flags ], _, _, _, _)) -> flags
| _ -> 0x0
@@ -1657,7 +1660,10 @@ let CombineSyntacticAndInferredArities g declKind rhsExpr prelimScheme =
// Don't use any expression information for members, where syntax dictates the arity completely
| _ when memberInfoOpt.IsSome ->
partialValReprInfoOpt
- | Some partialValReprInfoFromSyntax, true ->
+ // Don't use any expression information for 'let' bindings where return attributes are present
+ | _ when retAttribs.Length > 0 ->
+ partialValReprInfoOpt
+ | Some partialValReprInfoFromSyntax, true ->
let (PartialValReprInfo(curriedArgInfosFromSyntax, retInfoFromSyntax)) = partialValReprInfoFromSyntax
let partialArityInfo =
if isMutable then
@@ -2045,12 +2051,12 @@ module GeneralizationHelpers =
/// Recursively knock out typars we can't generalize.
/// For non-generalized type variables be careful to iteratively knock out
/// both the typars and any typars free in the constraints of the typars
- /// into the set that are considered free in the environment.
- let rec TrimUngeneralizableTypars genConstrainedTyparFlag inlineFlag (generalizedTypars: Typar list) freeInEnv =
- // Do not generalize type variables with a static requirement unless function is marked 'inline'
- let generalizedTypars, ungeneralizableTypars1 =
- if inlineFlag = ValInline.PseudoVal then generalizedTypars, []
- else generalizedTypars |> List.partition (fun tp -> tp.StaticReq = TyparStaticReq.None)
+ /// into the set that are considered free in the environment.
+ let rec TrimUngeneralizableTypars genConstrainedTyparFlag inlineFlag (generalizedTypars: Typar list) freeInEnv =
+ // Do not generalize type variables with a static requirement unless function is marked 'inline'
+ let generalizedTypars, ungeneralizableTypars1 =
+ if inlineFlag = ValInline.Always then generalizedTypars, []
+ else generalizedTypars |> List.partition (fun tp -> tp.StaticReq = TyparStaticReq.None)
// Do not generalize type variables which would escape their scope
// because they are free in the environment
@@ -2234,11 +2240,11 @@ let ComputeInlineFlag (memFlagsOption: SynMemberFlags option) isInline isMutable
if isMutable ||
(match memFlagsOption with
| None -> false
- | Some x -> (x.MemberKind = SynMemberKind.Constructor) || x.IsDispatchSlot || x.IsOverrideOrExplicitImpl)
- then ValInline.Never
- elif isInline then ValInline.PseudoVal
+ | Some x -> (x.MemberKind = SynMemberKind.Constructor) || x.IsDispatchSlot || x.IsOverrideOrExplicitImpl)
+ then ValInline.Never
+ elif isInline then ValInline.Always
else ValInline.Optional
- if isInline && (inlineFlag <> ValInline.PseudoVal) then
+ if isInline && (inlineFlag <> ValInline.Always) then
errorR(Error(FSComp.SR.tcThisValueMayNotBeInlined(), m))
inlineFlag
@@ -2314,10 +2320,10 @@ type IsObjExprBinding =
| ValOrMemberBinding
module BindingNormalization =
- /// Push a bunch of pats at once. They may contain patterns, e.g. let f (A x) (B y) = ...
- /// In this case the semantics is let f a b = let A x = a in let B y = b
- let private PushMultiplePatternsToRhs (cenv: cenv) isMember ps (NormalizedBindingRhs(spatsL, rtyOpt, rhsExpr)) =
- let spatsL2, rhsExpr = PushCurriedPatternsToExpr cenv.synArgNameGenerator rhsExpr.Range isMember ps rhsExpr
+ /// Push a bunch of pats at once. They may contain patterns, e.g. let f (A x) (B y) = ...
+ /// In this case the semantics is let f a b = let A x = a in let B y = b
+ let private PushMultiplePatternsToRhs (cenv: cenv) isMember pats (NormalizedBindingRhs(spatsL, rtyOpt, rhsExpr)) =
+ let spatsL2, rhsExpr = PushCurriedPatternsToExpr cenv.synArgNameGenerator rhsExpr.Range isMember pats rhsExpr
NormalizedBindingRhs(spatsL2@spatsL, rtyOpt, rhsExpr)
@@ -3083,12 +3089,13 @@ let CompilePatternForMatch cenv (env: TcEnv) mExpr matchm warnOnUnused actionOnF
mkAndSimplifyMatch DebugPointAtBinding.NoneAtInvisible mExpr matchm resultTy dtree targets
/// Compile a pattern
-let CompilePatternForMatchClauses cenv env mExpr matchm warnOnUnused actionOnFailure inputExprOpt inputTy resultTy tclauses =
- // Avoid creating a dummy in the common cases where we are about to bind a name for the expression
- // CLEANUP: avoid code duplication with code further below, i.e.all callers should call CompilePatternForMatch
- match tclauses with
- | [TClause(TPat_as (pat1, PBind (asVal, TypeScheme(generalizedTypars, _)), _), None, TTarget(vs, e, spTarget), m2)] ->
- let expr = CompilePatternForMatch cenv env mExpr matchm warnOnUnused actionOnFailure (asVal, generalizedTypars, None) [TClause(pat1, None, TTarget(ListSet.remove valEq asVal vs, e, spTarget), m2)] inputTy resultTy
+let CompilePatternForMatchClauses cenv env mExpr matchm warnOnUnused actionOnFailure inputExprOpt inputTy resultTy tclauses =
+ // Avoid creating a dummy in the common cases where we are about to bind a name for the expression
+ // CLEANUP: avoid code duplication with code further below, i.e.all callers should call CompilePatternForMatch
+ match tclauses with
+ | [TClause(TPat_as (pat1, PBind (asVal, TypeScheme(generalizedTypars, _)), _), None, TTarget(vs, e, spTarget, _), m2)] ->
+ let vs2 = ListSet.remove valEq asVal vs
+ let expr = CompilePatternForMatch cenv env mExpr matchm warnOnUnused actionOnFailure (asVal, generalizedTypars, None) [TClause(pat1, None, TTarget(vs2, e, spTarget, None), m2)] inputTy resultTy
asVal, expr
| _ ->
let matchValueTmp, _ = mkCompGenLocal mExpr "matchValue" inputTy
@@ -3350,9 +3357,9 @@ let EliminateInitializationGraphs
| Expr.TyLambda (_, _, b, _, _) -> CheckExpr st b
| Expr.Obj (_, ty, _, e, overrides, extraImpls, _) ->
- // NOTE: we can't fixup recursive references inside delegates since the closure delegee of a delegate is not accessible
- // from outside. Object expressions implementing interfaces can, on the other hand, be fixed up. See FSharp 1.0 bug 1469
- if isInterfaceTy g ty then
+ // NOTE: we can't fixup recursive references inside delegates since the closure delegee of a delegate is not accessible
+ // from outside. Object expressions implementing interfaces can, on the other hand, be fixed up. See FSharp 1.0 bug 1469
+ if isInterfaceTy g ty then
List.iter (fun (TObjExprMethod(_, _, _, _, e, _)) -> checkDelayed st e) overrides
List.iter (snd >> List.iter (fun (TObjExprMethod(_, _, _, _, e, _)) -> checkDelayed st e)) extraImpls
else
@@ -3393,12 +3400,14 @@ let EliminateInitializationGraphs
| Expr.Quote _ -> ()
| Expr.WitnessArg (_witnessInfo, _m) -> ()
- and CheckBinding st (TBind(_, e, _)) = CheckExpr st e
+ and CheckBinding st (TBind(_, e, _)) = CheckExpr st e
+
and CheckDecisionTree st = function
| TDSwitch(e1, csl, dflt, _) -> CheckExpr st e1; List.iter (fun (TCase(_, d)) -> CheckDecisionTree st d) csl; Option.iter (CheckDecisionTree st) dflt
| TDSuccess (es, _) -> es |> List.iter (CheckExpr st)
| TDBind(bind, e) -> CheckBinding st bind; CheckDecisionTree st e
- and CheckDecisionTreeTarget st (TTarget(_, e, _)) = CheckExpr st e
+
+ and CheckDecisionTreeTarget st (TTarget(_, e, _, _)) = CheckExpr st e
and CheckExprOp st op m =
match op with
@@ -3516,7 +3525,7 @@ let CheckAndRewriteObjectCtor g env (ctorLambdaExpr: Expr) =
let ty = tyOfExpr g recdExpr
let thisExpr = mkGetArg0 m ty
let setExpr = mkRefCellSet g m ty (exprForValRef m (mkLocalValRef safeInitVal)) thisExpr
- Expr.Sequential (recdExpr, setExpr, ThenDoSeq, DebugPointAtSequential.StmtOnly, m)
+ Expr.Sequential (recdExpr, setExpr, ThenDoSeq, DebugPointAtSequential.SuppressStmt, m)
let recdExpr =
match ctorInfo.safeInitInfo with
| NoSafeInitInfo -> recdExpr
@@ -3525,7 +3534,7 @@ let CheckAndRewriteObjectCtor g env (ctorLambdaExpr: Expr) =
let thisExpr = mkGetArg0 m thisTy
let thisTyInst = argsOfAppTy g thisTy
let setExpr = mkRecdFieldSetViaExprAddr (thisExpr, rfref, thisTyInst, mkOne g m, m)
- Expr.Sequential (recdExpr, setExpr, ThenDoSeq, DebugPointAtSequential.StmtOnly, m)
+ Expr.Sequential (recdExpr, setExpr, ThenDoSeq, DebugPointAtSequential.SuppressStmt, m)
recdExpr
@@ -3544,8 +3553,10 @@ let CheckAndRewriteObjectCtor g env (ctorLambdaExpr: Expr) =
// = "let pat = expr in "
| Expr.Let (bind, body, m, _) -> mkLetBind m bind (checkAndRewrite body)
- // The constructor is a sequence "let pat = expr in "
- | Expr.Match (spBind, a, b, targets, c, d) -> Expr.Match (spBind, a, b, (targets |> Array.map (fun (TTarget(vs, body, spTarget)) -> TTarget(vs, checkAndRewrite body, spTarget))), c, d)
+ // The constructor is a sequence "let pat = expr in "
+ | Expr.Match (spBind, a, b, targets, c, d) ->
+ let targets = targets |> Array.map (fun (TTarget(vs, body, spTarget, flags)) -> TTarget(vs, checkAndRewrite body, spTarget, flags))
+ Expr.Match (spBind, a, b, targets, c, d)
// = "let rec binds in "
| Expr.LetRec (a, body, _, _) -> Expr.LetRec (a, checkAndRewrite body, m, Construct.NewFreeVarsCache())
@@ -5849,12 +5860,28 @@ and TcIteratedLambdas cenv isFirst (env: TcEnv) overallTy takenNames tpenv e =
let envinner, _, vspecMap = MakeAndPublishSimpleValsForMergedScope cenv env m names
let byrefs = vspecMap |> Map.map (fun _ v -> isByrefTy cenv.g v.Type, v)
let envinner = if isMember then envinner else ExitFamilyRegion envinner
+ let vspecs = vs |> List.map (fun nm -> NameMap.find nm vspecMap)
+
+ // Match up the arginfos with the generated arguments and apply any information extracted from the attributes
+ let envinner =
+ match envinner.eLambdaArgInfos with
+ | infos :: rest ->
+ if infos.Length = vspecs.Length then
+ (vspecs, infos) ||> List.iter2 (fun v argInfo ->
+ let inlineIfLambda = HasFSharpAttribute cenv.g cenv.g.attrib_InlineIfLambdaAttribute argInfo.Attribs
+ if inlineIfLambda then
+ v.SetInlineIfLambda())
+ { envinner with eLambdaArgInfos = rest }
+ | [] -> envinner
+
let bodyExpr, tpenv = TcIteratedLambdas cenv false envinner resultTy takenNames tpenv bodyExpr
// See bug 5758: Non-monotonicity in inference: need to ensure that parameters are never inferred to have byref type, instead it is always declared
byrefs |> Map.iter (fun _ (orig, v) ->
if not orig && isByrefTy cenv.g v.Type then errorR(Error(FSComp.SR.tcParameterInferredByref v.DisplayName, v.Range)))
- mkMultiLambda m (List.map (fun nm -> NameMap.find nm vspecMap) vs) (bodyExpr, resultTy), tpenv
- | e ->
+
+ mkMultiLambda m vspecs (bodyExpr, resultTy), tpenv
+
+ | e ->
// Dive into the expression to check for syntax errors and suppress them if they show.
conditionallySuppressErrorReporting (not isFirst && synExprContainsError e) (fun () ->
//TcExprFlex cenv true true overallTy env tpenv e)
@@ -6936,7 +6963,7 @@ and TcRecdExpr cenv overallTy env tpenv (inherits, optOrigExpr, flds, mWholeExpr
if isFSharpObjModelTy cenv.g overallTy then errorR(Error(FSComp.SR.tcTypeIsNotARecordTypeNeedConstructor(), mWholeExpr))
elif not (isRecdTy cenv.g overallTy) then errorR(Error(FSComp.SR.tcTypeIsNotARecordType(), mWholeExpr))
- let superTy, tpenv =
+ let superInitExprOpt , tpenv =
match inherits, GetSuperTypeOfType cenv.g cenv.amap mWholeExpr overallTy with
| Some (superTy, arg, m, _, _), Some realSuperTy ->
// Constructor expression, with an explicit 'inheritedTys clause. Check the inherits clause.
@@ -6955,9 +6982,9 @@ and TcRecdExpr cenv overallTy env tpenv (inherits, optOrigExpr, flds, mWholeExpr
let expr, tpenv = TcRecordConstruction cenv overallTy env tpenv optOrigExprInfo overallTy fldsList mWholeExpr
let expr =
- match superTy with
+ match superInitExprOpt with
| _ when isStructTy cenv.g overallTy -> expr
- | Some e -> mkCompGenSequential mWholeExpr e expr
+ | Some superInitExpr -> mkCompGenSequential mWholeExpr superInitExpr expr
| None -> expr
expr, tpenv
@@ -7204,10 +7231,10 @@ and TcForEachExpr cenv overallTy env tpenv (pat, enumSynExpr, bodySynExpr, mWhol
// Add the pattern match compilation
let bodyExpr =
let valsDefinedByMatching = ListSet.remove valEq elemVar vspecs
- CompilePatternForMatch
- cenv env enumSynExpr.Range pat.Range false IgnoreWithWarning (elemVar, [], None)
- [TClause(pat, None, TTarget(valsDefinedByMatching, bodyExpr, DebugPointForTarget.Yes), mForLoopStart)]
- enumElemTy
+ CompilePatternForMatch
+ cenv env enumSynExpr.Range pat.Range false IgnoreWithWarning (elemVar, [], None)
+ [TClause(pat, None, TTarget(valsDefinedByMatching, bodyExpr, DebugPointForTarget.Yes, None), mForLoopStart)]
+ enumElemTy
overallTy
// Apply the fixup to bind the elemVar if needed
@@ -7267,8 +7294,7 @@ and TcQuotationExpr cenv overallTy env tpenv (_oper, raw, ast, isFromQueryExpres
// We serialize the quoted expression to bytes in IlxGen after type inference etc. is complete.
expr, tpenv
-
-/// When checking sequence of function applications,
+/// When checking sequence of function applications,
/// type applications and dot-notation projections, first extract known
/// type information from the applications.
///
@@ -9192,7 +9218,7 @@ and TcMatchClause cenv inputTy resultTy env isFirst tpenv (SynMatchClause(pat, o
let pat', optWhenExpr', vspecs, envinner, tpenv = TcMatchPattern cenv inputTy env tpenv (pat, optWhenExpr)
let resultEnv = if isFirst then envinner else { envinner with eContextInfo = ContextInfo.FollowingPatternMatchClause e.Range }
let e', tpenv = TcExprThatCanBeCtorBody cenv resultTy resultEnv tpenv e
- TClause(pat', optWhenExpr', TTarget(vspecs, e', spTgt), patm), tpenv
+ TClause(pat', optWhenExpr', TTarget(vspecs, e', spTgt, None), patm), tpenv
and TcStaticOptimizationConstraint cenv env tpenv c =
match c with
@@ -9366,7 +9392,20 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt
let argAndRetAttribs = ArgAndRetAttribs(argAttribs, retAttribs)
- if HasFSharpAttribute cenv.g cenv.g.attrib_DefaultValueAttribute valAttribs then
+ // See RFC FS-1087, the 'Zero' method of a builder may have 'DefaultValueAttribute' indicating it should
+ // always be used for empty branches of if/then/else and others
+ let isZeroMethod =
+ match declKind, pat with
+ | ModuleOrMemberBinding, SynPat.Named(id, _, _, _) when id.idText = "Zero" ->
+ match memberFlagsOpt with
+ | Some memberFlags ->
+ match memberFlags.MemberKind with
+ | SynMemberKind.Member -> true
+ | _ -> false
+ | _ -> false
+ | _ -> false
+
+ if HasFSharpAttribute cenv.g cenv.g.attrib_DefaultValueAttribute valAttribs && not isZeroMethod then
errorR(Error(FSComp.SR.tcDefaultValueAttributeRequiresVal(), mBinding))
let isThreadStatic = isThreadOrContextStatic cenv.g valAttribs
@@ -9460,6 +9499,10 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt
conditionallySuppressErrorReporting atTopNonLambdaDefn (fun () ->
+ // Save the arginfos away to match them up in the lambda
+ let (PartialValReprInfo(argInfos, _)) = partialValReprInfo
+ let envinner = { envinner with eLambdaArgInfos = argInfos }
+
if isCtor then TcExprThatIsCtorBody (safeThisValOpt, safeInitInfo) cenv overallExprTy envinner tpenv rhsExpr
else TcExprThatCantBeCtorBody cenv overallExprTy envinner tpenv rhsExpr)
@@ -9808,7 +9851,7 @@ and TcLetBinding cenv isUse env containerInfo declKind tpenv (synBinds, synBinds
// Don't introduce temporary or 'let' for 'match against wild' or 'match against unit'
| (TPat_wild _ | TPat_const (Const.Unit, _)) when not isUse && not isFixed && isNil generalizedTypars ->
- let mkSequentialBind (tm, tmty) = (mkSequential DebugPointAtSequential.Both m rhsExpr tm, tmty)
+ let mkSequentialBind (tm, tmty) = (mkSequential DebugPointAtSequential.SuppressNeither m rhsExpr tm, tmty)
(buildExpr >> mkSequentialBind, env, tpenv)
| _ ->
@@ -9859,7 +9902,7 @@ and TcLetBinding cenv isUse env containerInfo declKind tpenv (synBinds, synBinds
// Add the compilation of the pattern to the bodyExpr we get from mkCleanup
let mkPatBind (bodyExpr, bodyExprTy) =
let valsDefinedByMatching = ListSet.remove valEq patternInputTmp allValsDefinedByPattern
- let clauses = [TClause(checkedPat2, None, TTarget(valsDefinedByMatching, bodyExpr, DebugPointForTarget.No), m)]
+ let clauses = [TClause(checkedPat2, None, TTarget(valsDefinedByMatching, bodyExpr, DebugPointForTarget.No, None), m)]
let matchx = CompilePatternForMatch cenv env m m true ThrowIncompleteMatchException (patternInputTmp, generalizedTypars, Some rhsExpr) clauses tauTy bodyExprTy
let matchx = if (DeclKind.ConvertToLinearBindings declKind) then LinearizeTopMatch cenv.g altActualParent matchx else matchx
matchx, bodyExprTy
diff --git a/src/fsharp/CheckExpressions.fsi b/src/fsharp/CheckExpressions.fsi
index fa33a268406..7aa9e041747 100644
--- a/src/fsharp/CheckExpressions.fsi
+++ b/src/fsharp/CheckExpressions.fsi
@@ -98,6 +98,9 @@ type TcEnv =
eCtorInfo: CtorInfo option
eCallerMemberName: string option
+
+ // Active arg infos in iterated lambdas , allowing us to determine the attributes of arguments
+ eLambdaArgInfos: ArgReprInfo list list
}
member DisplayEnv : DisplayEnv
diff --git a/src/fsharp/CompilerDiagnostics.fs b/src/fsharp/CompilerDiagnostics.fs
index bcbe770d69b..5dc0d50628d 100644
--- a/src/fsharp/CompilerDiagnostics.fs
+++ b/src/fsharp/CompilerDiagnostics.fs
@@ -374,7 +374,8 @@ let warningOn err level specificWarnOn =
match n with
| 1182 -> false // chkUnusedValue - off by default
| 3180 -> false // abImplicitHeapAllocation - off by default
- | _ -> level >= GetWarningLevel err
+ | 3517 -> false // optFailedToInlineSuggestedValue - off by default
+ | _ -> level >= GetWarningLevel err
let SplitRelatedDiagnostics(err: PhasedDiagnostic) : PhasedDiagnostic * PhasedDiagnostic list =
let ToPhased e = {Exception=e; Phase = err.Phase}
diff --git a/src/fsharp/DetupleArgs.fs b/src/fsharp/DetupleArgs.fs
index 609ab4c202f..6bd70e56bab 100644
--- a/src/fsharp/DetupleArgs.fs
+++ b/src/fsharp/DetupleArgs.fs
@@ -288,7 +288,7 @@ module GlobalUsageAnalysis =
let context = []
recognise context origExpr
- let targetIntercept exprF z = function TTarget(_argvs, body, _) -> Some (foldUnderLambda exprF z body)
+ let targetIntercept exprF z = function TTarget(_argvs, body, _, _) -> Some (foldUnderLambda exprF z body)
let tmethodIntercept exprF z = function TObjExprMethod(_, _, _, _, e, _m) -> Some (foldUnderLambda exprF z e)
{ExprFolder0 with
@@ -300,7 +300,6 @@ module GlobalUsageAnalysis =
tmethodIntercept = tmethodIntercept
}
-
//-------------------------------------------------------------------------
// GlobalUsageAnalysis - entry point
//-------------------------------------------------------------------------
@@ -310,7 +309,6 @@ module GlobalUsageAnalysis =
let z = FoldImplFile folder z0 expr
z
-
let internalError str = raise(Failure(str))
let mkLocalVal m name ty topValInfo =
diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt
index 4cc83b0aec1..5b45a4451fb 100644
--- a/src/fsharp/FSComp.txt
+++ b/src/fsharp/FSComp.txt
@@ -1524,6 +1524,7 @@ featurePackageManagement,"package management"
featureFromEndSlicing,"from-end slicing"
featureFixedIndexSlice3d4d,"fixed-index slice 3d/4d"
featureAndBang,"applicative computation expressions"
+featureResumableStateMachines,"resumable state machines"
featureNullableOptionalInterop,"nullable optional interop"
featureDefaultInterfaceMemberConsumption,"default interface member consumption"
featureStringInterpolation,"string interpolation"
@@ -1565,4 +1566,26 @@ forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable
3390,xmlDocMissingParameter,"This XML comment is incomplete: no documentation for parameter '%s'"
3391,tcLiteralAttributeCannotUseActivePattern,"A [] declaration cannot use an active pattern for its identifier"
3392,containerDeprecated,"The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead."
-3393,containerSigningUnsupportedOnThisPlatform,"Key container signing is not supported on this platform."
\ No newline at end of file
+3393,containerSigningUnsupportedOnThisPlatform,"Key container signing is not supported on this platform."
+3401,ilxgenInvalidConstructInStateMachineDuringCodegen,"The resumable code construct '%s' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code."
+3402,tcInvalidResumableConstruct,"The construct '%s' may only be used in valid resumable code."
+3501,tcResumableCodeFunctionMustBeInline,"Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'"
+3501,tcResumableCodeArgMustHaveRightName,"Invalid resumable code. Resumable code parameter must have name beginning with '__expand'"
+3501,tcResumableCodeArgMustHaveRightKind,"Invalid resumable code. A resumable code parameter must be of delegate or function type"
+3501,tcResumableCodeContainsLetRec,"Invalid resumable code. A 'let rec' occured in the resumable code specification"
+3510,tcResumableCodeNotSupported,"Using resumable code or resumable state machines requires /langversion:preview"
+3511,reprStateMachineNotCompilable,"This state machine is not statically compilable. %s. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning."
+3512,reprStateMachineNotCompilableNoAlternative,"This state machine is not statically compilable and no alternative is available. %s. Use an 'if __useResumableCode then else ' to give an alternative."
+3513,tcResumableCodeInvocation,"Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code."
+reprResumableCodeInvokeNotReduced,"A resumable code invocation at '%s' could not be reduced"
+reprResumableCodeContainsLetRec,"A 'let rec' occured in the resumable code specification"
+reprResumableCodeContainsDynamicResumeAtInBody,"A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method"
+reprResumableCodeContainsResumptionInTryFinally,"A try/finally may not contain resumption points"
+reprResumableCodeContainsResumptionInHandlerOrFilter,"The 'with' block of a try/with may not contain resumption points"
+reprResumableCodeContainsFastIntegerForLoop,"A fast integer for loop may not contain resumption points"
+reprResumableCodeValueHasNoDefinition,"The resumable code value(s) '%s' does not have a definition"
+reprResumableCodeDefinitionWasGeneric,"A delegate or function producing resumable code in a state machine has type parameters"
+reprStateMachineInvalidForm,"The state machine has an unexpected form"
+3517,optFailedToInlineSuggestedValue,"The value '%s' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only."
+3518,implMissingInlineIfLambda,"The 'InlineIfLambda' attribute is present in the signature but not the implementation."
+3519,tcInlineIfLambdaUsedOnNonInlineFunctionOrMethod,"The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type."
diff --git a/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
index ff2d0cd0770..6f40101419c 100644
--- a/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
+++ b/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj
@@ -502,7 +502,7 @@
TypedTree\CompilerGlobalState.fs
- TypedTree\TypedTree.fs
+ TypedTree\TypedTreeBasics.fs
TypedTree\TypedTreeBasics.fsi
@@ -675,6 +675,12 @@
Optimize\LowerCallsAndSeqs.fs
+
+ Optimize\LowerStateMachines.fsi
+
+
+ Optimize\LowerStateMachines.fs
+
Optimize\autobox.fsi
diff --git a/src/fsharp/FSharp.Compiler.Service/StandardError.txt b/src/fsharp/FSharp.Compiler.Service/StandardError.txt
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/src/fsharp/FSharp.Compiler.Service/StandardOutput.txt b/src/fsharp/FSharp.Compiler.Service/StandardOutput.txt
new file mode 100644
index 00000000000..307a3493b91
--- /dev/null
+++ b/src/fsharp/FSharp.Compiler.Service/StandardOutput.txt
@@ -0,0 +1 @@
+5.0.104
diff --git a/src/fsharp/FSharp.Core/FSharp.Core.fsproj b/src/fsharp/FSharp.Core/FSharp.Core.fsproj
index f7267fe0d84..4fafe661c48 100644
--- a/src/fsharp/FSharp.Core/FSharp.Core.fsproj
+++ b/src/fsharp/FSharp.Core/FSharp.Core.fsproj
@@ -1,16 +1,30 @@
-
+
Library
- netstandard2.0
+ netstandard2.1;netstandard2.0$(NoWarn);45;55;62;75;1204true$(DefineConstants);FSHARP_COREBUILDING_WITH_LKG;$(DefineConstants)
- $(OtherFlags) --warnon:3218 --warnon:1182 --warnon:3390 --compiling-fslib --compiling-fslib-40 --maxerrors:100 --extraoptimizationloops:1 --nowarn:57
- true
+
+ $(OtherFlags) --warnon:3218
+
+ $(OtherFlags) --warnon:1182
+
+ $(OtherFlags) --warnon:3390
+
+ $(OtherFlags) --nowarn:57
+
+
+ $(OtherFlags) --nowarn:3511 --nowarn:3513
+ $(OtherFlags) --compiling-fslib --compiling-fslib-40 --maxerrors:100 --extraoptimizationloops:1
+
+ preview
+
+ truetruetrue
@@ -162,12 +176,24 @@
Control/event.fs
+
+ Control/resumable.fsi
+
+
+ Control/resumable.fs
+
Control/async.fsi
Control/async.fs
+
+ Control/tasks.fsi
+
+
+ Control/tasks.fs
+
Control/eventmodule.fsi
diff --git a/src/fsharp/FSharp.Core/FSharp.Core.nuspec b/src/fsharp/FSharp.Core/FSharp.Core.nuspec
index f2cd1cf86b0..4efa4c5c661 100644
--- a/src/fsharp/FSharp.Core/FSharp.Core.nuspec
+++ b/src/fsharp/FSharp.Core/FSharp.Core.nuspec
@@ -5,6 +5,7 @@
en-US
+
@@ -19,5 +20,12 @@
+
+
+
+
+
+
+
diff --git a/src/fsharp/FSharp.Core/array.fs b/src/fsharp/FSharp.Core/array.fs
index d66792beec3..407ac4aff78 100644
--- a/src/fsharp/FSharp.Core/array.fs
+++ b/src/fsharp/FSharp.Core/array.fs
@@ -238,7 +238,7 @@ namespace Microsoft.FSharp.Collections
res
[]
- let inline iter action (array: 'T[]) =
+ let inline iter ([] action) (array: 'T[]) =
checkNonNull "array" array
for i = 0 to array.Length-1 do
action array.[i]
@@ -258,7 +258,7 @@ namespace Microsoft.FSharp.Collections
Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 0 i temp
[]
- let inline map (mapping: 'T -> 'U) (array: 'T[]) =
+ let inline map ([] mapping: 'T -> 'U) (array: 'T[]) =
checkNonNull "array" array
let res: 'U[] = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked array.Length
for i = 0 to res.Length-1 do
@@ -1110,7 +1110,7 @@ namespace Microsoft.FSharp.Collections
acc
[]
- let inline minBy projection (array: _[]) =
+ let inline minBy ([] projection) (array: _[]) =
checkNonNull "array" array
if array.Length = 0 then invalidArg "array" LanguagePrimitives.ErrorStrings.InputArrayEmptyString
let mutable accv = array.[0]
@@ -1167,7 +1167,7 @@ namespace Microsoft.FSharp.Collections
LanguagePrimitives.DivideByInt< ^U> acc array.Length
[]
- let inline compareWith (comparer: 'T -> 'T -> int) (array1: 'T[]) (array2: 'T[]) =
+ let inline compareWith ([] comparer: 'T -> 'T -> int) (array1: 'T[]) (array2: 'T[]) =
checkNonNull "array1" array1
checkNonNull "array2" array2
diff --git a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs
index 7d05c29b497..5fd4e32b29e 100644
--- a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs
+++ b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs
@@ -268,6 +268,11 @@ module ExtraTopLevelOperators =
[]
[]
[]
+ #if !BUILDING_WITH_LKG && !BUILD_FROM_SOURCE
+ []
+ []
+ []
+ #endif
[]
[]
do()
diff --git a/src/fsharp/FSharp.Core/prim-types.fs b/src/fsharp/FSharp.Core/prim-types.fs
index 319481c6814..02b11b4bb42 100644
--- a/src/fsharp/FSharp.Core/prim-types.fs
+++ b/src/fsharp/FSharp.Core/prim-types.fs
@@ -108,7 +108,7 @@ namespace Microsoft.FSharp.Core
inherit System.Attribute()
member x.Value = value
- []
+ []
[]
type DefaultValueAttribute(check:bool) =
inherit System.Attribute()
@@ -261,6 +261,11 @@ namespace Microsoft.FSharp.Core
member x.Message = message
+ []
+ []
+ type InlineIfLambdaAttribute() =
+ inherit System.Attribute()
+
[]
[]
type CompilationArgumentCountsAttribute(counts:int[]) =
diff --git a/src/fsharp/FSharp.Core/prim-types.fsi b/src/fsharp/FSharp.Core/prim-types.fsi
index 2ed374eb5ce..baa9a5127b2 100644
--- a/src/fsharp/FSharp.Core/prim-types.fsi
+++ b/src/fsharp/FSharp.Core/prim-types.fsi
@@ -520,7 +520,7 @@ namespace Microsoft.FSharp.Core
///
///
/// Attributes
- []
+ []
[]
type DefaultValueAttribute =
inherit Attribute
@@ -773,6 +773,22 @@ namespace Microsoft.FSharp.Core
/// Indicates the warning message to be emitted when F# source code uses this construct
member Message: string
+ /// Adding this attribute to a parameter of function type indicates that, if the overall function or method is inlined and the parameter is
+ /// determined to be a known lambda, then this function should be statically inlined throughout the body of the function of method.
+ ///
+ /// If the function parameter is called multiple times in the implementation of the function or method this attribute may cause code explosion and slow compilation times.
+ ///
+ /// Attributes
+ []
+ []
+ []
+ type InlineIfLambdaAttribute =
+ inherit Attribute
+
+ /// Creates an instance of the attribute
+ /// InlineIfLambdaAttribute
+ new : unit -> InlineIfLambdaAttribute
+
/// This attribute is generated automatically by the F# compiler to tag functions and members
/// that accept a partial application of some of their arguments and return a residual function.
///
diff --git a/src/fsharp/FSharp.Core/resumable.fs b/src/fsharp/FSharp.Core/resumable.fs
new file mode 100644
index 00000000000..caddd62fe91
--- /dev/null
+++ b/src/fsharp/FSharp.Core/resumable.fs
@@ -0,0 +1,375 @@
+// Original notice:
+// To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights
+// to this software to the public domain worldwide. This software is distributed without any warranty.
+//
+// Updates:
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+#nowarn "9"
+#nowarn "51"
+namespace Microsoft.FSharp.Core.CompilerServices
+
+#if !BUILDING_WITH_LKG && !BUILD_FROM_SOURCE
+open System
+open System.Runtime.CompilerServices
+open Microsoft.FSharp.Core
+open Microsoft.FSharp.Core
+open Microsoft.FSharp.Core.Printf
+open Microsoft.FSharp.Core.CompilerServices
+open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
+open Microsoft.FSharp.Control
+open Microsoft.FSharp.Collections
+
+type IResumableStateMachine<'Data> =
+ abstract ResumptionPoint: int
+ abstract Data: 'Data with get, set
+
+/// Acts as a template for struct state machines introduced by __stateMachine, and also as a reflective implementation
+[]
+type ResumableStateMachine<'Data> =
+
+ []
+ val mutable Data: 'Data
+
+ []
+ val mutable ResumptionPoint: int
+
+ /// Represents the delegated runtime continuation of a resumable state machine created dynamically
+ []
+ val mutable ResumptionDynamicInfo: ResumptionDynamicInfo<'Data>
+
+ interface IResumableStateMachine<'Data> with
+ member sm.ResumptionPoint = sm.ResumptionPoint
+ member sm.Data with get() = sm.Data and set v = sm.Data <- v
+
+ interface IAsyncStateMachine with
+
+ // Used for dynamic execution. For "__stateMachine" it is replaced.
+ member sm.MoveNext() =
+ sm.ResumptionDynamicInfo.MoveNext(&sm)
+
+ // Used when dynamic execution. For "__stateMachine" it is replaced.
+ member sm.SetStateMachine(state) =
+ sm.ResumptionDynamicInfo.SetStateMachine(&sm, state)
+
+and ResumptionFunc<'Data> = delegate of byref> -> bool
+
+and []
+ ResumptionDynamicInfo<'Data>(initial: ResumptionFunc<'Data>) =
+ member val ResumptionFunc: ResumptionFunc<'Data> = initial with get, set
+ member val ResumptionData: obj = null with get, set
+ abstract MoveNext: machine: byref> -> unit
+ abstract SetStateMachine: machine: byref> * machineState: IAsyncStateMachine -> unit
+
+type ResumableCode<'Data, 'T> = delegate of byref> -> bool
+
+/// Defines the implementation of the MoveNext method for a struct state machine.
+[]
+type MoveNextMethodImpl<'Data> = delegate of byref> -> unit
+
+/// Defines the implementation of the SetStateMachine method for a struct state machine.
+[]
+type SetStateMachineMethodImpl<'Data> = delegate of byref> * IAsyncStateMachine -> unit
+
+/// Defines the implementation of the code reun after the creation of a struct state machine.
+[]
+type AfterCode<'Data, 'Result> = delegate of byref> -> 'Result
+
+[]
+module StateMachineHelpers =
+
+ /// Statically determines whether resumable code is being used
+ []
+ let __useResumableCode<'T> : bool = false
+
+ []
+ let __resumableEntry () : int option =
+ failwith "__resumableEntry should always be guarded by __useResumableCode and only used in valid state machine implementations"
+
+ []
+ let __resumeAt<'T> (programLabel: int) : 'T =
+ ignore programLabel
+ failwith "__resumeAt should always be guarded by __useResumableCode and only used in valid state machine implementations"
+
+ []
+ let __stateMachine<'Data, 'Result>
+ (moveNextMethod: MoveNextMethodImpl<'Data>)
+ (setStateMachineMethod: SetStateMachineMethodImpl<'Data>)
+ (afterCode: AfterCode<'Data, 'Result>): 'Result =
+ ignore moveNextMethod
+ ignore setStateMachineMethod
+ ignore afterCode
+ failwith "__stateMachine should always be guarded by __useResumableCode and only used in valid state machine implementations"
+
+module ResumableCode =
+
+ let inline SetResumptionFunc (sm: byref>) f =
+ sm.ResumptionDynamicInfo.ResumptionFunc <- f
+
+ let inline GetResumptionFunc (sm: byref>) =
+ sm.ResumptionDynamicInfo.ResumptionFunc
+
+ let inline Delay(f : unit -> ResumableCode<'Data, 'T>) : ResumableCode<'Data, 'T> =
+ ResumableCode<'Data, 'T>(fun sm -> (f()).Invoke(&sm))
+
+ /// Used to represent no-ops like the implicit empty "else" branch of an "if" expression.
+ let inline Zero() : ResumableCode<'Data, unit> =
+ ResumableCode<'Data, unit>(fun sm -> true)
+
+ /// Chains together a step with its following step.
+ /// Note that this requires that the first step has no result.
+ /// This prevents constructs like `task { return 1; return 2; }`.
+ let CombineDynamic(sm: byref>, code1: ResumableCode<'Data, unit>, code2: ResumableCode<'Data, 'T>) : bool =
+ if code1.Invoke(&sm) then
+ code2.Invoke(&sm)
+ else
+ let rec resume (mf: ResumptionFunc<'Data>) =
+ ResumptionFunc<'Data>(fun sm ->
+ if mf.Invoke(&sm) then
+ code2.Invoke(&sm)
+ else
+ sm.ResumptionDynamicInfo.ResumptionFunc <- (resume (GetResumptionFunc &sm))
+ false)
+
+ sm.ResumptionDynamicInfo.ResumptionFunc <- (resume (GetResumptionFunc &sm))
+ false
+
+ /// Chains together a step with its following step.
+ /// Note that this requires that the first step has no result.
+ /// This prevents constructs like `task { return 1; return 2; }`.
+ let inline Combine(code1: ResumableCode<'Data, unit>, code2: ResumableCode<'Data, 'T>) : ResumableCode<'Data, 'T> =
+ ResumableCode<'Data, 'T>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ // NOTE: The code for code1 may contain await points! Resuming may branch directly
+ // into this code!
+ let __stack_fin = code1.Invoke(&sm)
+ if __stack_fin then
+ code2.Invoke(&sm)
+ else
+ false
+ //-- RESUMABLE CODE END
+ else
+ CombineDynamic(&sm, code1, code2))
+
+ let rec WhileDynamic (sm: byref>, condition: unit -> bool, body: ResumableCode<'Data,unit>) : bool =
+ if condition() then
+ if body.Invoke (&sm) then
+ WhileDynamic (&sm, condition, body)
+ else
+ let rf = GetResumptionFunc &sm
+ sm.ResumptionDynamicInfo.ResumptionFunc <- (ResumptionFunc<'Data>(fun sm -> WhileBodyDynamicAux(&sm, condition, body, rf)))
+ false
+ else
+ true
+ and WhileBodyDynamicAux (sm: byref>, condition: unit -> bool, body: ResumableCode<'Data,unit>, rf: ResumptionFunc<_>) : bool =
+ if rf.Invoke (&sm) then
+ WhileDynamic (&sm, condition, body)
+ else
+ let rf = GetResumptionFunc &sm
+ sm.ResumptionDynamicInfo.ResumptionFunc <- (ResumptionFunc<'Data>(fun sm -> WhileBodyDynamicAux(&sm, condition, body, rf)))
+ false
+
+ /// Builds a step that executes the body while the condition predicate is true.
+ let inline While ([] condition : unit -> bool, body : ResumableCode<'Data, unit>) : ResumableCode<'Data, unit> =
+ ResumableCode<'Data, unit>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ let mutable __stack_go = true
+ while __stack_go && condition() do
+ // NOTE: The body of the state machine code for 'while' may contain await points, so resuming
+ // the code will branch directly into the expanded 'body', branching directly into the while loop
+ let __stack_body_fin = body.Invoke(&sm)
+ // If the body completed, we go back around the loop (__stack_go = true)
+ // If the body yielded, we yield (__stack_go = false)
+ __stack_go <- __stack_body_fin
+ __stack_go
+ //-- RESUMABLE CODE END
+ else
+ WhileDynamic(&sm, condition, body))
+
+ let rec TryWithDynamic (sm: byref>, body: ResumableCode<'Data, 'T>, handler: exn -> ResumableCode<'Data, 'T>) : bool =
+ try
+ if body.Invoke(&sm) then
+ true
+ else
+ let rf = GetResumptionFunc &sm
+ sm.ResumptionDynamicInfo.ResumptionFunc <- (ResumptionFunc<'Data>(fun sm -> TryWithDynamic(&sm, ResumableCode<'Data,'T>(fun sm -> rf.Invoke(&sm)), handler)))
+ false
+ with exn ->
+ (handler exn).Invoke(&sm)
+
+ /// Wraps a step in a try/with. This catches exceptions both in the evaluation of the function
+ /// to retrieve the step, and in the continuation of the step (if any).
+ let inline TryWith (body: ResumableCode<'Data, 'T>, catch: exn -> ResumableCode<'Data, 'T>) : ResumableCode<'Data, 'T> =
+ ResumableCode<'Data, 'T>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ let mutable __stack_fin = false
+ let mutable __stack_caught = false
+ let mutable __stack_savedExn = Unchecked.defaultof<_>
+ // This is a meaningless assignment but ensures a debug point gets laid down
+ // at the 'try' in the try/with for code as we enter into the handler.
+ __stack_fin <- __stack_fin || __stack_fin
+ try
+ // The try block may contain await points.
+ let __stack_body_fin = body.Invoke(&sm)
+ // If we make it to the assignment we prove we've made a step
+ __stack_fin <- __stack_body_fin
+ with exn ->
+ // Note, remarkExpr in the F# compiler detects this pattern as the code
+ // is inlined and elides the debug sequence point on the code. This is because the inlining will associate
+ // the sequence point with the 'try' of the TryFinally because that is the range
+ // given for the whole expression
+ // task.TryWith(....)
+ // If you change this code you should check debug sequence points and the generated
+ // code tests for try/with in tasks.
+ __stack_caught <- true
+ __stack_savedExn <- exn
+
+ if __stack_caught then
+ // Place the catch code outside the catch block
+ (catch __stack_savedExn).Invoke(&sm)
+ else
+ __stack_fin
+ //-- RESUMABLE CODE END
+
+ else
+ TryWithDynamic(&sm, body, catch))
+
+ let rec TryFinallyCompensateDynamic (sm: byref>, mf: ResumptionFunc<'Data>, savedExn: exn option) : bool =
+ let mutable fin = false
+ fin <- mf.Invoke(&sm)
+ if fin then
+ // reraise at the end of the finally block
+ match savedExn with
+ | None -> true
+ | Some exn -> raise exn
+ else
+ let rf = GetResumptionFunc &sm
+ sm.ResumptionDynamicInfo.ResumptionFunc <- (ResumptionFunc<'Data>(fun sm -> TryFinallyCompensateDynamic(&sm, rf, savedExn)))
+ false
+
+ let rec TryFinallyAsyncDynamic (sm: byref>, body: ResumableCode<'Data, 'T>, compensation: ResumableCode<'Data,unit>) : bool =
+ let mutable fin = false
+ let mutable savedExn = None
+ try
+ fin <- body.Invoke(&sm)
+ with exn ->
+ savedExn <- Some exn
+ fin <- true
+ if fin then
+ TryFinallyCompensateDynamic(&sm, ResumptionFunc<'Data>(fun sm -> compensation.Invoke(&sm)), savedExn)
+ else
+ let rf = GetResumptionFunc &sm
+ sm.ResumptionDynamicInfo.ResumptionFunc <- (ResumptionFunc<'Data>(fun sm -> TryFinallyAsyncDynamic(&sm, ResumableCode<'Data,'T>(fun sm -> rf.Invoke(&sm)), compensation)))
+ false
+
+ /// Wraps a step in a try/finally. This catches exceptions both in the evaluation of the function
+ /// to retrieve the step, and in the continuation of the step (if any).
+ let inline TryFinally (body: ResumableCode<'Data, 'T>, compensation: ResumableCode<'Data,unit>) =
+ ResumableCode<'Data, 'T>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ let mutable __stack_fin = false
+ // This is a meaningless assignment but ensures a debug point gets laid down
+ // at the 'try' in the try/finally. The 'try' is used as the range for the
+ // F# computation expression desugaring to 'TryFinally' and this range in turn gets applied
+ // to inlined code.
+ __stack_fin <- __stack_fin || __stack_fin
+ try
+ let __stack_body_fin = body.Invoke(&sm)
+ // If we make it to the assignment we prove we've made a step, an early 'ret' exit out of the try/with
+ // may skip this step.
+ __stack_fin <- __stack_body_fin
+ with _exn ->
+ // Note, remarkExpr in the F# compiler detects this pattern as the code
+ // is inlined and elides the debug sequence point on either the 'compensation'
+ // 'reraise' statement for the code. This is because the inlining will associate
+ // the sequence point with the 'try' of the TryFinally because that is the range
+ // given for the whole expression
+ // task.TryFinally(....)
+ // If you change this code you should check debug sequence points and the generated
+ // code tests for try/finally in tasks.
+ let __stack_ignore = compensation.Invoke(&sm)
+ reraise()
+
+ if __stack_fin then
+ let __stack_ignore = compensation.Invoke(&sm)
+ ()
+ __stack_fin
+ //-- RESUMABLE CODE END
+ else
+ TryFinallyAsyncDynamic(&sm, body, ResumableCode<_,_>(fun sm -> compensation.Invoke(&sm))))
+
+ /// Wraps a step in a try/finally. This catches exceptions both in the evaluation of the function
+ /// to retrieve the step, and in the continuation of the step (if any).
+ let inline TryFinallyAsync (body: ResumableCode<'Data, 'T>, compensation: ResumableCode<'Data,unit>) : ResumableCode<'Data, 'T> =
+ ResumableCode<'Data, 'T>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ let mutable __stack_fin = false
+ let mutable savedExn = None
+ // This is a meaningless assignment but ensures a debug point gets laid down
+ // at the 'try' in the try/finally. The 'try' is used as the range for the
+ // F# computation expression desugaring to 'TryFinally' and this range in turn gets applied
+ // to inlined code.
+ __stack_fin <- __stack_fin || __stack_fin
+ try
+ let __stack_body_fin = body.Invoke(&sm)
+ // If we make it to the assignment we prove we've made a step, an early 'ret' exit out of the try/with
+ // may skip this step.
+ __stack_fin <- __stack_body_fin
+ with exn ->
+ savedExn <- Some exn
+ __stack_fin <- true
+
+ if __stack_fin then
+ let __stack_compensation_fin = compensation.Invoke(&sm)
+ __stack_fin <- __stack_compensation_fin
+ if __stack_fin then
+ match savedExn with
+ | None -> ()
+ | Some exn -> raise exn
+ __stack_fin
+ //-- RESUMABLE CODE END
+ else
+ TryFinallyAsyncDynamic(&sm, body, compensation))
+
+ let inline Using (resource : 'Resource, body : 'Resource -> ResumableCode<'Data, 'T>) : ResumableCode<'Data, 'T> when 'Resource :> IDisposable =
+ // A using statement is just a try/finally with the finally block disposing if non-null.
+ TryFinally(
+ ResumableCode<'Data, 'T>(fun sm -> (body resource).Invoke(&sm)),
+ ResumableCode<'Data,unit>(fun sm ->
+ if not (isNull (box resource)) then
+ resource.Dispose()
+ true))
+
+ let inline For (sequence : seq<'T>, body : 'T -> ResumableCode<'Data, unit>) : ResumableCode<'Data, unit> =
+ // A for loop is just a using statement on the sequence's enumerator...
+ Using (sequence.GetEnumerator(),
+ // ... and its body is a while loop that advances the enumerator and runs the body on each element.
+ (fun e -> While((fun () -> e.MoveNext()), ResumableCode<'Data, unit>(fun sm -> (body e.Current).Invoke(&sm)))))
+
+ let YieldDynamic (sm: byref>) : bool =
+ let cont = ResumptionFunc<'Data>(fun _sm -> true)
+ sm.ResumptionDynamicInfo.ResumptionFunc <- cont
+ false
+
+ let inline Yield () : ResumableCode<'Data, unit> =
+ ResumableCode<'Data, unit>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ match __resumableEntry() with
+ | Some contID ->
+ sm.ResumptionPoint <- contID
+ //if verbose then printfn $"[{sm.Id}] Yield: returning false to indicate yield, contID = {contID}"
+ false
+ | None ->
+ //if verbose then printfn $"[{sm.Id}] Yield: returning true to indicate post-yield"
+ true
+ //-- RESUMABLE CODE END
+ else
+ YieldDynamic(&sm))
+
+#endif
diff --git a/src/fsharp/FSharp.Core/resumable.fsi b/src/fsharp/FSharp.Core/resumable.fsi
new file mode 100644
index 00000000000..a7b4ff17ef1
--- /dev/null
+++ b/src/fsharp/FSharp.Core/resumable.fsi
@@ -0,0 +1,204 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.FSharp.Core.CompilerServices
+
+#if !BUILDING_WITH_LKG && !BUILD_FROM_SOURCE
+open Microsoft.FSharp.Collections
+open Microsoft.FSharp.Core
+open System
+open System.Runtime.CompilerServices
+
+/// Acts as a template for struct state machines introduced by __stateMachine, and also as a reflective implementation
+[]
+[]
+type ResumableStateMachine<'Data> =
+
+ /// When statically compiled, holds the data for the state machine
+ []
+ val mutable Data: 'Data
+
+ /// When statically compiled, holds the continuation goto-label further execution of the state machine
+ []
+ val mutable ResumptionPoint: int
+
+ /// Represents the delegated runtime continuation for a resumable state machine created dynamically
+ /// This field is removed from state machines generated using '__stateMachine'. Resumable code
+ /// used in state machines which accesses this field will raise a runtime exception.
+ []
+ val mutable ResumptionDynamicInfo: ResumptionDynamicInfo<'Data>
+
+ interface IResumableStateMachine<'Data>
+
+ interface IAsyncStateMachine
+
+and
+ []
+ IResumableStateMachine<'Data> =
+ /// Get the resumption point of the state machine
+ abstract ResumptionPoint: int
+
+ /// Copy-out or copy-in the data of the state machine
+ abstract Data: 'Data with get, set
+
+/// Represents the delegated runtime continuation of a resumable state machine created dynamically
+and
+ []
+ ResumptionDynamicInfo<'Data> =
+
+ /// Create dynamic information for a state machine
+ new: initial: ResumptionFunc<'Data> -> ResumptionDynamicInfo<'Data>
+
+ /// The continuation of the state machine
+ member ResumptionFunc: ResumptionFunc<'Data> with get, set
+
+ /// Additional data associated with the state machine
+ member ResumptionData: obj with get, set
+
+ /// Executes the MoveNext implementation of the state machine
+ abstract MoveNext: machine: byref> -> unit
+
+ /// Executes the SetStateMachine implementation of the state machine
+ abstract SetStateMachine: machine: byref> * machineState: IAsyncStateMachine -> unit
+
+/// Represents the runtime continuation of a resumable state machine created dynamically
+and []
+ ResumptionFunc<'Data> = delegate of byref> -> bool
+
+/// A special compiler-recognised delegate type for specifying blocks of resumable code
+/// with access to the state machine.
+[]
+type ResumableCode<'Data, 'T> = delegate of byref> -> bool
+
+/// Contains functions for composing resumable code blocks
+[]
+[]
+module ResumableCode =
+
+ /// Sequences one section of resumable code after another
+ []
+ val inline Combine: code1: ResumableCode<'Data, unit> * code2: ResumableCode<'Data, 'T> -> ResumableCode<'Data, 'T>
+
+ /// Creates resumable code whose definition is a delayed function
+ []
+ val inline Delay: f: (unit -> ResumableCode<'Data, 'T>) -> ResumableCode<'Data, 'T>
+
+ /// Specifies resumable code which iterates an input sequence
+ []
+ val inline For: sequence: seq<'T> * body: ('T -> ResumableCode<'Data, unit>) -> ResumableCode<'Data, unit>
+
+ /// Specifies resumable code which iterates yields
+ []
+ val inline Yield: unit -> ResumableCode<'Data, unit>
+
+ /// Specifies resumable code which executes with try/finally semantics
+ []
+ val inline TryFinally: body: ResumableCode<'Data, 'T> * compensation: ResumableCode<'Data,unit> -> ResumableCode<'Data, 'T>
+
+ /// Specifies resumable code which executes with try/finally semantics
+ []
+ val inline TryFinallyAsync: body: ResumableCode<'Data, 'T> * compensation: ResumableCode<'Data,unit> -> ResumableCode<'Data, 'T>
+
+ /// Specifies resumable code which executes with try/with semantics
+ []
+ val inline TryWith: body: ResumableCode<'Data, 'T> * catch: (exn -> ResumableCode<'Data, 'T>) -> ResumableCode<'Data, 'T>
+
+ /// Specifies resumable code which executes with 'use' semantics
+ []
+ val inline Using: resource: 'Resource * body: ('Resource -> ResumableCode<'Data, 'T>) -> ResumableCode<'Data, 'T> when 'Resource :> IDisposable
+
+ /// Specifies resumable code which executes a loop
+ []
+ val inline While: [] condition: (unit -> bool) * body: ResumableCode<'Data, unit> -> ResumableCode<'Data, unit>
+
+ /// Specifies resumable code which does nothing
+ []
+ val inline Zero: unit -> ResumableCode<'Data, unit>
+
+ /// The dynamic implementation of the corresponding operation. This operation should not be used directly.
+ []
+ val CombineDynamic: sm: byref> * code1: ResumableCode<'Data, unit> * code2: ResumableCode<'Data, 'T> -> bool
+
+ /// The dynamic implementation of the corresponding operation. This operation should not be used directly.
+ []
+ val WhileDynamic: sm: byref> * condition: (unit -> bool) * body: ResumableCode<'Data, unit> -> bool
+
+ /// The dynamic implementation of the corresponding operation. This operation should not be used directly.
+ []
+ val TryFinallyAsyncDynamic: sm: byref> * body: ResumableCode<'Data, 'T> * compensation: ResumableCode<'Data,unit> -> bool
+
+ /// The dynamic implementation of the corresponding operation. This operation should not be used directly.
+ []
+ val TryWithDynamic: sm: byref> * body: ResumableCode<'Data, 'T> * handler: (exn -> ResumableCode<'Data, 'T>) -> bool
+
+ /// The dynamic implementation of the corresponding operation. This operation should not be used directly.
+ []
+ val YieldDynamic: sm: byref> -> bool
+
+/// Defines the implementation of the MoveNext method for a struct state machine.
+[]
+type MoveNextMethodImpl<'Data> = delegate of byref> -> unit
+
+/// Defines the implementation of the SetStateMachine method for a struct state machine.
+[]
+type SetStateMachineMethodImpl<'Data> = delegate of byref> * IAsyncStateMachine -> unit
+
+/// Defines the implementation of the code run after the creation of a struct state machine.
+[]
+type AfterCode<'Data, 'Result> = delegate of byref> -> 'Result
+
+/// Contains compiler intrinsics related to the definition of state machines.
+[]
+module StateMachineHelpers =
+
+ ///
+ /// When used in a conditional, statically determines whether the 'then' branch
+ /// represents valid resumable code and provides an alternative implementation
+ /// if not.
+ ///
+ []
+ []
+ val __useResumableCode<'T> : bool
+
+ ///
+ /// Indicates a resumption point within resumable code
+ ///
+ []
+ []
+ val __resumableEntry: unit -> int option
+
+ ///
+ /// Indicates to jump to a resumption point within resumable code.
+ /// This may be the first statement in a MoveNextMethodImpl.
+ /// The integer must be a valid resumption point within this resumable code.
+ ///
+ ///
+ []
+ []
+ val __resumeAt : programLabel: int -> 'T
+
+ ///
+ /// Statically generates a closure struct type based on ResumableStateMachine,
+ /// At runtime an instance of the new struct type is populated and 'afterMethod' is called
+ /// to consume it.
+ ///
+ ///
+ ///
+ /// At compile-time, the ResumableStateMachine type guides the generation of a new struct type by the F# compiler
+ /// with closure-capture fields in a way similar to an object expression.
+ /// Any mention of the ResumableStateMachine type in any the 'methods' is rewritten to this
+ /// fresh struct type. The 'methods' are used to implement the interfaces on ResumableStateMachine and are also rewritten.
+ /// The 'after' method is then executed and must eliminate the ResumableStateMachine. For example,
+ /// its return type must not include ResumableStateMachine.
+ ///
+ /// Gives the implementation of the MoveNext method on IAsyncStateMachine.
+ /// Gives the implementation of the SetStateMachine method on IAsyncStateMachine.
+ /// Gives code to execute after the generation of the state machine and to produce the final result.
+ []
+ []
+ val __stateMachine<'Data, 'Result> :
+ moveNextMethod: MoveNextMethodImpl<'Data> ->
+ setStateMachineMethod: SetStateMachineMethodImpl<'Data> ->
+ afterCode: AfterCode<'Data, 'Result>
+ -> 'Result
+
+#endif
diff --git a/src/fsharp/FSharp.Core/seqcore.fs b/src/fsharp/FSharp.Core/seqcore.fs
index 5a6e8142960..d4fa9b99de3 100644
--- a/src/fsharp/FSharp.Core/seqcore.fs
+++ b/src/fsharp/FSharp.Core/seqcore.fs
@@ -147,6 +147,7 @@ namespace Microsoft.FSharp.Core.CompilerServices
open Microsoft.FSharp.Primitives.Basics
open System.Collections
open System.Collections.Generic
+ open System.Runtime.CompilerServices
module RuntimeHelpers =
diff --git a/src/fsharp/FSharp.Core/seqcore.fsi b/src/fsharp/FSharp.Core/seqcore.fsi
index 9a0d9bd1055..cce9b5dbb3e 100644
--- a/src/fsharp/FSharp.Core/seqcore.fsi
+++ b/src/fsharp/FSharp.Core/seqcore.fsi
@@ -60,9 +60,10 @@ namespace Microsoft.FSharp.Core.CompilerServices
open System
open System.Collections
open System.Collections.Generic
+ open System.Runtime.CompilerServices
open Microsoft.FSharp.Core
open Microsoft.FSharp.Collections
-
+
[]
/// A group of functions used as part of the compiled representation of F# sequence expressions.
module RuntimeHelpers =
@@ -128,20 +129,25 @@ namespace Microsoft.FSharp.Core.CompilerServices
///
/// A new sequence generator for the expression.
new : unit -> GeneratedSequenceBase<'T>
+
/// The F# compiler emits implementations of this type for compiled sequence expressions.
///
/// A new enumerator for the sequence.
abstract GetFreshEnumerator : unit -> IEnumerator<'T>
+
/// The F# compiler emits implementations of this type for compiled sequence expressions.
///
/// A reference to the sequence.
///
/// A 0, 1, and 2 respectively indicate Stop, Yield, and Goto conditions for the sequence generator.
abstract GenerateNext : result:byref> -> int
+
/// The F# compiler emits implementations of this type for compiled sequence expressions.
abstract Close: unit -> unit
+
/// The F# compiler emits implementations of this type for compiled sequence expressions.
abstract CheckClose: bool
+
/// The F# compiler emits implementations of this type for compiled sequence expressions.
abstract LastGenerated : 'T
interface IEnumerable<'T>
diff --git a/src/fsharp/FSharp.Core/tasks.fs b/src/fsharp/FSharp.Core/tasks.fs
new file mode 100644
index 00000000000..e0032a24aaa
--- /dev/null
+++ b/src/fsharp/FSharp.Core/tasks.fs
@@ -0,0 +1,384 @@
+// Task builder for F# that compiles to allocation-free paths for synchronous code.
+//
+// Originally written in 2016 by Robert Peele (humbobst@gmail.com)
+// New operator-based overload resolution for F# 4.0 compatibility by Gustavo Leon in 2018.
+// Revised for insertion into FSHarp.Core by Microsoft, 2019.
+//
+// Original notice:
+// To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights
+// to this software to the public domain worldwide. This software is distributed without any warranty.
+//
+// Updates:
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.FSharp.Control
+
+ #if !BUILDING_WITH_LKG && !BUILD_FROM_SOURCE
+ open System
+ open System.Runtime.CompilerServices
+ open System.Threading
+ open System.Threading.Tasks
+ open Microsoft.FSharp.Core
+ open Microsoft.FSharp.Core.CompilerServices
+ open Microsoft.FSharp.Core.CompilerServices.StateMachineHelpers
+ open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
+ open Microsoft.FSharp.Control
+ open Microsoft.FSharp.Collections
+
+ /// The extra data stored in ResumableStateMachine for tasks
+ []
+ type TaskStateMachineData<'T> =
+
+ []
+ val mutable Result : 'T
+
+ []
+ val mutable MethodBuilder : AsyncTaskMethodBuilder<'T>
+
+ and TaskStateMachine<'TOverall> = ResumableStateMachine>
+ and TaskResumptionFunc<'TOverall> = ResumptionFunc>
+ and TaskResumptionDynamicInfo<'TOverall> = ResumptionDynamicInfo>
+ and TaskCode<'TOverall, 'T> = ResumableCode, 'T>
+
+ type TaskBuilderBase() =
+
+ member inline _.Delay(f : unit -> TaskCode<'TOverall, 'T>) : TaskCode<'TOverall, 'T> =
+ TaskCode<'TOverall, 'T>(fun sm -> (f()).Invoke(&sm))
+
+ /// Used to represent no-ops like the implicit empty "else" branch of an "if" expression.
+ []
+ member inline _.Zero() : TaskCode<'TOverall, unit> = ResumableCode.Zero()
+
+ member inline _.Return (value: 'T) : TaskCode<'T, 'T> =
+ TaskCode<'T, _>(fun sm ->
+ sm.Data.Result <- value
+ true)
+
+ /// Chains together a step with its following step.
+ /// Note that this requires that the first step has no result.
+ /// This prevents constructs like `task { return 1; return 2; }`.
+ member inline _.Combine(task1: TaskCode<'TOverall, unit>, task2: TaskCode<'TOverall, 'T>) : TaskCode<'TOverall, 'T> =
+ ResumableCode.Combine(task1, task2)
+
+ /// Builds a step that executes the body while the condition predicate is true.
+ member inline _.While ([] condition : unit -> bool, body : TaskCode<'TOverall, unit>) : TaskCode<'TOverall, unit> =
+ ResumableCode.While(condition, body)
+
+ /// Wraps a step in a try/with. This catches exceptions both in the evaluation of the function
+ /// to retrieve the step, and in the continuation of the step (if any).
+ member inline _.TryWith (body: TaskCode<'TOverall, 'T>, catch: exn -> TaskCode<'TOverall, 'T>) : TaskCode<'TOverall, 'T> =
+ ResumableCode.TryWith(body, catch)
+
+ /// Wraps a step in a try/finally. This catches exceptions both in the evaluation of the function
+ /// to retrieve the step, and in the continuation of the step (if any).
+ member inline _.TryFinally (body: TaskCode<'TOverall, 'T>, [] compensation : unit -> unit) : TaskCode<'TOverall, 'T> =
+ ResumableCode.TryFinally(body, ResumableCode<_,_>(fun _sm -> compensation(); true))
+
+ member inline _.For (sequence : seq<'T>, body : 'T -> TaskCode<'TOverall, unit>) : TaskCode<'TOverall, unit> =
+ ResumableCode.For(sequence, body)
+
+ #if NETSTANDARD2_1
+ member inline internal this.TryFinallyAsync(body: TaskCode<'TOverall, 'T>, compensation : unit -> ValueTask) : TaskCode<'TOverall, 'T> =
+ ResumableCode.TryFinallyAsync(body, ResumableCode<_,_>(fun sm ->
+ if __useResumableCode then
+ let mutable __stack_condition_fin = true
+ let __stack_vtask = compensation()
+ if not __stack_vtask.IsCompleted then
+ let mutable awaiter = __stack_vtask.GetAwaiter()
+ let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm)
+ __stack_condition_fin <- __stack_yield_fin
+
+ if not __stack_condition_fin then
+ sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm)
+
+ __stack_condition_fin
+ else
+ let vtask = compensation()
+ let mutable awaiter = vtask.GetAwaiter()
+
+ let cont =
+ TaskResumptionFunc<'TOverall>( fun sm ->
+ awaiter.GetResult() |> ignore
+ true)
+
+ // shortcut to continue immediately
+ if awaiter.IsCompleted then
+ true
+ else
+ sm.ResumptionDynamicInfo.ResumptionData <- (awaiter :> ICriticalNotifyCompletion)
+ sm.ResumptionDynamicInfo.ResumptionFunc <- cont
+ false
+ ))
+
+ member inline this.Using<'Resource, 'TOverall, 'T when 'Resource :> IAsyncDisposable> (resource: 'Resource, body: 'Resource -> TaskCode<'TOverall, 'T>) : TaskCode<'TOverall, 'T> =
+ this.TryFinallyAsync(
+ (fun sm -> (body resource).Invoke(&sm)),
+ (fun () ->
+ if not (isNull (box resource)) then
+ resource.DisposeAsync()
+ else
+ ValueTask()))
+ #endif
+
+
+ type TaskBuilder() =
+
+ inherit TaskBuilderBase()
+
+ // This is the dynamic implementation - this is not used
+ // for statically compiled tasks. An executor (resumptionFuncExecutor) is
+ // registered with the state machine, plus the initial resumption.
+ // The executor stays constant throughout the execution, it wraps each step
+ // of the execution in a try/with. The resumption is changed at each step
+ // to represent the continuation of the computation.
+ static member RunDynamic(code: TaskCode<'T, 'T>) : Task<'T> =
+ let mutable sm = TaskStateMachine<'T>()
+ let initialResumptionFunc = TaskResumptionFunc<'T>(fun sm -> code.Invoke(&sm))
+ let resumptionInfo =
+ { new TaskResumptionDynamicInfo<'T>(initialResumptionFunc) with
+ member info.MoveNext(sm) =
+ let mutable savedExn = null
+ try
+ sm.ResumptionDynamicInfo.ResumptionData <- null
+ let step = info.ResumptionFunc.Invoke(&sm)
+ if step then
+ sm.Data.MethodBuilder.SetResult(sm.Data.Result)
+ else
+ let mutable awaiter = sm.ResumptionDynamicInfo.ResumptionData :?> ICriticalNotifyCompletion
+ assert not (isNull awaiter)
+ sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm)
+
+ with exn ->
+ savedExn <- exn
+ // Run SetException outside the stack unwind, see https://github.com/dotnet/roslyn/issues/26567
+ match savedExn with
+ | null -> ()
+ | exn -> sm.Data.MethodBuilder.SetException exn
+
+ member _.SetStateMachine(sm, state) =
+ sm.Data.MethodBuilder.SetStateMachine(state)
+ }
+ sm.ResumptionDynamicInfo <- resumptionInfo
+ sm.Data.MethodBuilder <- AsyncTaskMethodBuilder<'T>.Create()
+ sm.Data.MethodBuilder.Start(&sm)
+ sm.Data.MethodBuilder.Task
+
+ static member inline Run(code : TaskCode<'T, 'T>) : Task<'T> =
+ if __useResumableCode then
+ __stateMachine, Task<'T>>
+ (MoveNextMethodImpl<_>(fun sm ->
+ //-- RESUMABLE CODE START
+ __resumeAt sm.ResumptionPoint
+ let mutable __stack_exn : Exception = null
+ try
+ let __stack_code_fin = code.Invoke(&sm)
+ if __stack_code_fin then
+ sm.Data.MethodBuilder.SetResult(sm.Data.Result)
+ with exn ->
+ __stack_exn <- exn
+ // Run SetException outside the stack unwind, see https://github.com/dotnet/roslyn/issues/26567
+ match __stack_exn with
+ | null -> ()
+ | exn -> sm.Data.MethodBuilder.SetException exn
+ //-- RESUMABLE CODE END
+ ))
+ (SetStateMachineMethodImpl<_>(fun sm state -> sm.Data.MethodBuilder.SetStateMachine(state)))
+ (AfterCode<_,_>(fun sm ->
+ sm.Data.MethodBuilder <- AsyncTaskMethodBuilder<'T>.Create()
+ sm.Data.MethodBuilder.Start(&sm)
+ sm.Data.MethodBuilder.Task))
+ else
+ TaskBuilder.RunDynamic(code)
+
+ member inline _.Run(code : TaskCode<'T, 'T>) : Task<'T> =
+ TaskBuilder.Run(code)
+
+ type BackgroundTaskBuilder() =
+
+ inherit TaskBuilderBase()
+
+ static member RunDynamic(code: TaskCode<'T, 'T>) : Task<'T> =
+ // backgroundTask { .. } escapes to a background thread where necessary
+ // See spec of ConfigureAwait(false) at https://devblogs.microsoft.com/dotnet/configureawait-faq/
+ if isNull SynchronizationContext.Current && obj.ReferenceEquals(TaskScheduler.Current, TaskScheduler.Default) then
+ TaskBuilder.RunDynamic(code)
+ else
+ Task.Run<'T>(fun () -> TaskBuilder.RunDynamic(code))
+
+ //// Same as TaskBuilder.Run except the start is inside Task.Run if necessary
+ member inline _.Run(code : TaskCode<'T, 'T>) : Task<'T> =
+ if __useResumableCode then
+ __stateMachine, Task<'T>>
+ (MoveNextMethodImpl<_>(fun sm ->
+ //-- RESUMABLE CODE START
+ __resumeAt sm.ResumptionPoint
+ try
+ let __stack_code_fin = code.Invoke(&sm)
+ if __stack_code_fin then
+ sm.Data.MethodBuilder.SetResult(sm.Data.Result)
+ with exn ->
+ sm.Data.MethodBuilder.SetException exn
+ //-- RESUMABLE CODE END
+ ))
+ (SetStateMachineMethodImpl<_>(fun sm state -> sm.Data.MethodBuilder.SetStateMachine(state)))
+ (AfterCode<_,Task<'T>>(fun sm ->
+ // backgroundTask { .. } escapes to a background thread where necessary
+ // See spec of ConfigureAwait(false) at https://devblogs.microsoft.com/dotnet/configureawait-faq/
+ if isNull SynchronizationContext.Current && obj.ReferenceEquals(TaskScheduler.Current, TaskScheduler.Default) then
+ sm.Data.MethodBuilder <- AsyncTaskMethodBuilder<'T>.Create()
+ sm.Data.MethodBuilder.Start(&sm)
+ sm.Data.MethodBuilder.Task
+ else
+ let sm = sm // copy contents of state machine so we can capture it
+ Task.Run<'T>(fun () ->
+ let mutable sm = sm // host local mutable copy of contents of state machine on this thread pool thread
+ sm.Data.MethodBuilder <- AsyncTaskMethodBuilder<'T>.Create()
+ sm.Data.MethodBuilder.Start(&sm)
+ sm.Data.MethodBuilder.Task)))
+ else
+ BackgroundTaskBuilder.RunDynamic(code)
+
+ module TaskBuilder =
+
+ let task = TaskBuilder()
+ let backgroundTask = BackgroundTaskBuilder()
+
+namespace Microsoft.FSharp.Control.TaskBuilderExtensions
+
+ open Microsoft.FSharp.Control
+ open System
+ open System.Runtime.CompilerServices
+ open System.Threading.Tasks
+ open Microsoft.FSharp.Core
+ open Microsoft.FSharp.Core.CompilerServices
+ open Microsoft.FSharp.Core.CompilerServices.StateMachineHelpers
+ open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
+
+ module LowPriority =
+ // Low priority extensions
+ type TaskBuilderBase with
+
+ static member inline BindDynamic< ^TaskLike, 'TResult1, 'TResult2, ^Awaiter , 'TOverall
+ when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
+ and ^Awaiter :> ICriticalNotifyCompletion
+ and ^Awaiter: (member get_IsCompleted: unit -> bool)
+ and ^Awaiter: (member GetResult: unit -> 'TResult1)>
+ (sm: byref<_>, task: ^TaskLike, continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)) : bool =
+
+ let mutable awaiter = (^TaskLike: (member GetAwaiter : unit -> ^Awaiter)(task))
+
+ let cont =
+ (TaskResumptionFunc<'TOverall>( fun sm ->
+ let result = (^Awaiter : (member GetResult : unit -> 'TResult1)(awaiter))
+ (continuation result).Invoke(&sm)))
+
+ // shortcut to continue immediately
+ if (^Awaiter : (member get_IsCompleted : unit -> bool)(awaiter)) then
+ cont.Invoke(&sm)
+ else
+ sm.ResumptionDynamicInfo.ResumptionData <- (awaiter :> ICriticalNotifyCompletion)
+ sm.ResumptionDynamicInfo.ResumptionFunc <- cont
+ false
+
+ member inline _.Bind< ^TaskLike, 'TResult1, 'TResult2, ^Awaiter , 'TOverall
+ when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
+ and ^Awaiter :> ICriticalNotifyCompletion
+ and ^Awaiter: (member get_IsCompleted: unit -> bool)
+ and ^Awaiter: (member GetResult: unit -> 'TResult1)>
+ (task: ^TaskLike, continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)) : TaskCode<'TOverall, 'TResult2> =
+
+ TaskCode<'TOverall, _>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ // Get an awaiter from the awaitable
+ let mutable awaiter = (^TaskLike: (member GetAwaiter : unit -> ^Awaiter)(task))
+
+ let mutable __stack_fin = true
+ if not (^Awaiter : (member get_IsCompleted : unit -> bool)(awaiter)) then
+ // This will yield with __stack_yield_fin = false
+ // This will resume with __stack_yield_fin = true
+ let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm)
+ __stack_fin <- __stack_yield_fin
+
+ if __stack_fin then
+ let result = (^Awaiter : (member GetResult : unit -> 'TResult1)(awaiter))
+ (continuation result).Invoke(&sm)
+ else
+ sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm)
+ false
+ else
+ TaskBuilderBase.BindDynamic< ^TaskLike, 'TResult1, 'TResult2, ^Awaiter , 'TOverall>(&sm, task, continuation)
+ //-- RESUMABLE CODE END
+ )
+
+ member inline this.ReturnFrom< ^TaskLike, ^Awaiter, 'T
+ when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
+ and ^Awaiter :> ICriticalNotifyCompletion
+ and ^Awaiter: (member get_IsCompleted: unit -> bool)
+ and ^Awaiter: (member GetResult: unit -> 'T)>
+ (task: ^TaskLike) : TaskCode< 'T, 'T> =
+
+ this.Bind(task, (fun v -> this.Return v))
+
+ member inline _.Using<'Resource, 'TOverall, 'T when 'Resource :> IDisposable> (resource: 'Resource, body: 'Resource -> TaskCode<'TOverall, 'T>) =
+ ResumableCode.Using(resource, body)
+
+ module HighPriority =
+ // High priority extensions
+ type TaskBuilderBase with
+ static member BindDynamic (sm: byref<_>, task: Task<'TResult1>, continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)) : bool =
+ let mutable awaiter = task.GetAwaiter()
+
+ let cont =
+ (TaskResumptionFunc<'TOverall>(fun sm ->
+ let result = awaiter.GetResult()
+ (continuation result).Invoke(&sm)))
+
+ // shortcut to continue immediately
+ if awaiter.IsCompleted then
+ cont.Invoke(&sm)
+ else
+ sm.ResumptionDynamicInfo.ResumptionData <- (awaiter :> ICriticalNotifyCompletion)
+ sm.ResumptionDynamicInfo.ResumptionFunc <- cont
+ false
+
+ member inline _.Bind (task: Task<'TResult1>, continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)) : TaskCode<'TOverall, 'TResult2> =
+
+ TaskCode<'TOverall, _>(fun sm ->
+ if __useResumableCode then
+ //-- RESUMABLE CODE START
+ // Get an awaiter from the task
+ let mutable awaiter = task.GetAwaiter()
+
+ let mutable __stack_fin = true
+ if not awaiter.IsCompleted then
+ // This will yield with __stack_yield_fin = false
+ // This will resume with __stack_yield_fin = true
+ let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm)
+ __stack_fin <- __stack_yield_fin
+ if __stack_fin then
+ let result = awaiter.GetResult()
+ (continuation result).Invoke(&sm)
+ else
+ sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm)
+ false
+ else
+ TaskBuilderBase.BindDynamic(&sm, task, continuation)
+ //-- RESUMABLE CODE END
+ )
+
+ member inline this.ReturnFrom (task: Task<'T>) : TaskCode<'T, 'T> =
+ this.Bind(task, (fun v -> this.Return v))
+
+ module MediumPriority =
+ open HighPriority
+
+ // Medium priority extensions
+ type TaskBuilderBase with
+ member inline this.Bind (computation: Async<'TResult1>, continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)) : TaskCode<'TOverall, 'TResult2> =
+ this.Bind (Async.StartAsTask computation, continuation)
+
+ member inline this.ReturnFrom (computation: Async<'T>) : TaskCode<'T, 'T> =
+ this.ReturnFrom (Async.StartAsTask computation)
+
+#endif
diff --git a/src/fsharp/FSharp.Core/tasks.fsi b/src/fsharp/FSharp.Core/tasks.fsi
new file mode 100644
index 00000000000..e5ad31910ce
--- /dev/null
+++ b/src/fsharp/FSharp.Core/tasks.fsi
@@ -0,0 +1,311 @@
+// TaskBuilder.fs - TPL task computation expressions for F#
+//
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace Microsoft.FSharp.Control
+
+ #if !BUILDING_WITH_LKG && !BUILD_FROM_SOURCE
+ open System
+ open System.Runtime.CompilerServices
+ open System.Threading.Tasks
+ open Microsoft.FSharp.Core
+ open Microsoft.FSharp.Core.CompilerServices
+ open Microsoft.FSharp.Collections
+
+ []
+ []
+ []
+ ///
+ /// The extra data stored in ResumableStateMachine for tasks
+ ///
+ type TaskStateMachineData<'T> =
+
+ ///
+ /// Holds the final result of the state machine
+ ///
+ []
+ val mutable Result : 'T
+
+ ///
+ /// Holds the MethodBuilder for the state machine
+ ///
+ []
+ val mutable MethodBuilder : AsyncTaskMethodBuilder<'T>
+
+ ///
+ /// This is used by the compiler as a template for creating state machine structs
+ ///
+ and []
+ []
+ TaskStateMachine<'TOverall> = ResumableStateMachine>
+
+ ///
+ /// Represents the runtime continuation of a task state machine created dynamically
+ ///
+ and []
+ []
+ TaskResumptionFunc<'TOverall> = ResumptionFunc>
+
+ ///
+ /// A special compiler-recognised delegate type for specifying blocks of task code
+ /// with access to the state machine.
+ ///
+ and []
+ []
+ TaskCode<'TOverall, 'T> = ResumableCode, 'T>
+
+ ///
+ /// Contains methods to build tasks using the F# computation expression syntax
+ ///
+ []
+ []
+ type TaskBuilderBase =
+
+ ///
+ /// Specifies the sequential composition of two units of task code.
+ ///
+ []
+ member inline Combine: task1: TaskCode<'TOverall, unit> * task2: TaskCode<'TOverall, 'T> -> TaskCode<'TOverall, 'T>
+
+ ///
+ /// Specifies the delayed execution of a unit of task code.
+ ///
+ []
+ member inline Delay: f: (unit -> TaskCode<'TOverall, 'T>) -> TaskCode<'TOverall, 'T>
+
+ ///
+ /// Specifies the iterative execution of a unit of task code.
+ ///
+ []
+ member inline For: sequence: seq<'T> * body: ('T -> TaskCode<'TOverall, unit>) -> TaskCode<'TOverall, unit>
+
+ ///
+ /// Specifies a unit of task code which returns a value
+ ///
+ []
+ member inline Return: value: 'T -> TaskCode<'T, 'T>
+
+ ///
+ /// Specifies a unit of task code which excuted using try/finally semantics
+ ///
+ []
+ member inline TryFinally: body: TaskCode<'TOverall, 'T> * [] compensation: (unit -> unit) -> TaskCode<'TOverall, 'T>
+
+ ///
+ /// Specifies a unit of task code which excuted using try/with semantics
+ ///
+ []
+ member inline TryWith: body: TaskCode<'TOverall, 'T> * catch: (exn -> TaskCode<'TOverall, 'T>) -> TaskCode<'TOverall, 'T>
+
+ #if NETSTANDARD2_1
+ ///
+ /// Specifies a unit of task code which binds to the resource implementing IAsyncDisposable and disposes it asynchronously
+ ///
+ []
+ member inline Using<'Resource, 'TOverall, 'T when 'Resource :> IAsyncDisposable> : resource: 'Resource * body: ('Resource -> TaskCode<'TOverall, 'T>) -> TaskCode<'TOverall, 'T>
+ #endif
+
+ ///
+ /// Specifies the iterative execution of a unit of task code.
+ ///
+ []
+ member inline While: condition: (unit -> bool) * body: TaskCode<'TOverall, unit> -> TaskCode<'TOverall, unit>
+
+ ///
+ /// Specifies a unit of task code which produces no result
+ ///
+ []
+ []
+ member inline Zero: unit -> TaskCode<'TOverall, unit>
+
+ ///
+ /// Contains methods to build tasks using the F# computation expression syntax
+ ///
+ []
+ []
+ type TaskBuilder =
+ inherit TaskBuilderBase
+
+ ///
+ /// The entry point for the dynamic implementation of the corresponding operation. Do not use directly, only used when executing quotations that involve tasks or other reflective execution of F# code.
+ ///
+ []
+ static member RunDynamic: code: TaskCode<'T, 'T> -> Task<'T>
+
+ /// Hosts the task code in a state machine and starts the task.
+ []
+ member inline Run: code: TaskCode<'T, 'T> -> Task<'T>
+
+ ///
+ /// Contains methods to build tasks using the F# computation expression syntax
+ ///
+ []
+ []
+ type BackgroundTaskBuilder =
+ inherit TaskBuilderBase
+
+ ///
+ /// The entry point for the dynamic implementation of the corresponding operation. Do not use directly, only used when executing quotations that involve tasks or other reflective execution of F# code.
+ ///
+ []
+ static member RunDynamic: code: TaskCode<'T, 'T> -> Task<'T>
+
+ ///
+ /// Hosts the task code in a state machine and starts the task, executing in the threadpool using Task.Run
+ ///
+ []
+ member inline Run: code: TaskCode<'T, 'T> -> Task<'T>
+
+ /// Contains the `task` computation expression builder.
+ []
+ []
+ module TaskBuilder =
+
+ ///
+ /// Builds a task using computation expression syntax.
+ ///
+ []
+ val task: TaskBuilder
+
+ ///
+ /// Builds a task using computation expression syntax which switches to execute on a background thread if not
+ /// already doing so.
+ ///
+ ///
+ /// If the task is created on a foreground thread (where is non-null)
+ /// its body is executed on a background thread using .
+ /// If created on a background thread (where is null) it is executed immeidately
+ /// immediately on that thread.
+ ///
+ []
+ val backgroundTask: BackgroundTaskBuilder
+
+
+/// Contains the `task` computation expression builder.
+namespace Microsoft.FSharp.Control.TaskBuilderExtensions
+
+ open System
+ open System.Runtime.CompilerServices
+ open System.Threading.Tasks
+ open Microsoft.FSharp.Core
+ open Microsoft.FSharp.Control
+
+ ///
+ /// Contains low-priority overloads for the `task` computation expression builder.
+ ///
+ //
+ // Note: they are low priority because they are auto-opened first, and F# has a rule
+ // that extension method opened later in sequence get higher priority
+ //
+ // AutoOpen is by assembly attribute to get sequencing of AutoOpen correct and
+ // so each gives different priority
+ []
+ module LowPriority =
+
+ type TaskBuilderBase with
+ ///
+ /// Specifies a unit of task code which draws a result from a task-like value
+ /// satisfying the GetAwaiter pattern and calls a continuation.
+ ///
+ []
+ member inline Bind< ^TaskLike, 'TResult1, 'TResult2, ^Awaiter, 'TOverall > :
+ task: ^TaskLike *
+ continuation: ( 'TResult1 -> TaskCode<'TOverall, 'TResult2>)
+ -> TaskCode<'TOverall, 'TResult2>
+ when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
+ and ^Awaiter :> ICriticalNotifyCompletion
+ and ^Awaiter: (member get_IsCompleted: unit -> bool)
+ and ^Awaiter: (member GetResult: unit -> 'TResult1)
+
+ ///
+ /// Specifies a unit of task code which draws its result from a task-like value
+ /// satisfying the GetAwaiter pattern.
+ ///
+ []
+ member inline ReturnFrom< ^TaskLike, ^Awaiter, 'T> :
+ task: ^TaskLike
+ -> TaskCode< 'T, 'T >
+ when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
+ and ^Awaiter :> ICriticalNotifyCompletion
+ and ^Awaiter: (member get_IsCompleted: unit -> bool)
+ and ^Awaiter: (member GetResult: unit -> 'T)
+
+ ///
+ /// The entry point for the dynamic implementation of the corresponding operation. Do not use directly, only used when executing quotations that involve tasks or other reflective execution of F# code.
+ ///
+ []
+ static member inline BindDynamic< ^TaskLike, 'TResult1, 'TResult2, ^Awaiter, 'TOverall > :
+ sm: byref> *
+ task: ^TaskLike *
+ continuation: ( 'TResult1 -> TaskCode<'TOverall, 'TResult2>)
+ -> bool
+ when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
+ and ^Awaiter :> ICriticalNotifyCompletion
+ and ^Awaiter: (member get_IsCompleted: unit -> bool)
+ and ^Awaiter: (member GetResult: unit -> 'TResult1)
+
+ ///
+ /// Specifies a unit of task code which binds to the resource implementing IDisposable and disposes it synchronously
+ ///
+ []
+ member inline Using: resource: 'Resource * body: ('Resource -> TaskCode<'TOverall, 'T>) -> TaskCode<'TOverall, 'T> when 'Resource :> IDisposable
+
+ ///
+ /// Contains medium-priority overloads for the `task` computation expression builder.
+ ///
+ []
+ module MediumPriority =
+
+ type TaskBuilderBase with
+
+ ///
+ /// Specifies a unit of task code which draws a result from an F# async value then calls a continuation.
+ ///
+ []
+ member inline Bind:
+ computation: Async<'TResult1> *
+ continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)
+ -> TaskCode<'TOverall, 'TResult2>
+
+ ///
+ /// Specifies a unit of task code which draws a result from an F# async value.
+ ///
+ []
+ member inline ReturnFrom:
+ computation: Async<'T>
+ -> TaskCode<'T, 'T>
+
+ ///
+ /// Contains high-priority overloads for the `task` computation expression builder.
+ ///
+ []
+ module HighPriority =
+
+ type TaskBuilderBase with
+ ///
+ /// Specifies a unit of task code which draws a result from a task then calls a continuation.
+ ///
+ []
+ member inline Bind:
+ task: Task<'TResult1> *
+ continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)
+ -> TaskCode<'TOverall, 'TResult2>
+
+ ///
+ /// Specifies a unit of task code which draws a result from a task.
+ ///
+ []
+ member inline ReturnFrom:
+ task: Task<'T>
+ -> TaskCode<'T, 'T>
+
+ ///
+ /// The entry point for the dynamic implementation of the corresponding operation. Do not use directly, only used when executing quotations that involve tasks or other reflective execution of F# code.
+ ///
+ []
+ static member BindDynamic:
+ sm: byref> *
+ task: Task<'TResult1> *
+ continuation: ('TResult1 -> TaskCode<'TOverall, 'TResult2>)
+ -> bool
+#endif
diff --git a/src/fsharp/FindUnsolved.fs b/src/fsharp/FindUnsolved.fs
index 3728ec59aa8..a4d284e879a 100644
--- a/src/fsharp/FindUnsolved.fs
+++ b/src/fsharp/FindUnsolved.fs
@@ -170,7 +170,7 @@ and accExprs cenv env exprs =
and accTargets cenv env m ty targets =
Array.iter (accTarget cenv env m ty) targets
-and accTarget cenv env _m _ty (TTarget(_vs, e, _)) =
+and accTarget cenv env _m _ty (TTarget(_vs, e, _, _)) =
accExpr cenv env e
and accDTree cenv env x =
diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs
index 3659f0e753a..f6747580250 100644
--- a/src/fsharp/IlxGen.fs
+++ b/src/fsharp/IlxGen.fs
@@ -23,9 +23,12 @@ open FSharp.Compiler.AbstractIL.ILX.Types
open FSharp.Compiler.AttributeChecking
open FSharp.Compiler.CompilerGlobalState
open FSharp.Compiler.ErrorLogger
+open FSharp.Compiler.Features
+open FSharp.Compiler.Infos
open FSharp.Compiler.Import
open FSharp.Compiler.Infos
open FSharp.Compiler.LowerCallsAndSeqs
+open FSharp.Compiler.LowerStateMachines
open FSharp.Compiler.Syntax
open FSharp.Compiler.Syntax.PrettyNaming
open FSharp.Compiler.SyntaxTreeOps
@@ -389,7 +392,13 @@ let ComputeTypeAccess (tref: ILTypeRef) hidden =
/// Indicates how type parameters are mapped to IL type variables
[]
-type TypeReprEnv(reprs: Map, count: int) =
+type TypeReprEnv(reprs: Map, count: int, templateReplacement: (TyconRef * ILType * TyparInst) option) =
+
+ static let empty = TypeReprEnv(count = 0, reprs = Map.empty, templateReplacement = None)
+ /// Get the template replacement information used when using struct types for state machines based on a "template" struct
+ member __.TemplateReplacement = templateReplacement
+
+ member __.WithTemplateReplacement(tcref, ilty, tpinst) = TypeReprEnv(reprs, count, Some (tcref, ilty, tpinst))
/// Lookup a type parameter
member _.Item (tp: Typar, m: range) =
@@ -403,7 +412,7 @@ type TypeReprEnv(reprs: Map, count: int) =
/// then it is ignored, since it doesn't correspond to a .NET type parameter.
member tyenv.AddOne (tp: Typar) =
if IsNonErasedTypar tp then
- TypeReprEnv(reprs.Add (tp.Stamp, uint16 count), count + 1)
+ TypeReprEnv(reprs.Add (tp.Stamp, uint16 count), count + 1, templateReplacement)
else
tyenv
@@ -415,20 +424,23 @@ type TypeReprEnv(reprs: Map, count: int) =
member _.Count = count
/// Get the empty environment, where no type parameters are in scope.
- static member Empty =
- TypeReprEnv(count = 0, reprs = Map.empty)
+ static member Empty = empty
+
+ /// Reset to the empty environment, where no type parameters are in scope.
+ member eenv.ResetTypars() =
+ TypeReprEnv(count = 0, reprs = Map.empty, templateReplacement = eenv.TemplateReplacement)
/// Get the environment for a fixed set of type parameters
- static member ForTypars tps =
- TypeReprEnv.Empty.Add tps
+ member eenv.ForTypars tps =
+ eenv.ResetTypars().Add tps
/// Get the environment for within a type definition
- static member ForTycon (tycon: Tycon) =
- TypeReprEnv.ForTypars (tycon.TyparsNoRange)
+ member eenv.ForTycon (tycon: Tycon) =
+ eenv.ForTypars (tycon.TyparsNoRange)
/// Get the environment for generating a reference to items within a type definition
- static member ForTyconRef (tycon: TyconRef) =
- TypeReprEnv.ForTycon tycon.Deref
+ member eenv.ForTyconRef (tycon: TyconRef) =
+ eenv.ForTycon tycon.Deref
//--------------------------------------------------------------------------
// Generate type references
@@ -496,10 +508,12 @@ and GenILTyAppAux amap m tyenv (tref, boxity, ilTypeOpt) tinst =
| Some ilType ->
ilType // monomorphic types include a cached ilType to avoid reallocation of an ILType node
-and GenNamedTyAppAux (amap: ImportMap) m tyenv ptrsOK tcref tinst =
+and GenNamedTyAppAux (amap: ImportMap) m (tyenv: TypeReprEnv) ptrsOK tcref tinst =
let g = amap.g
+ match tyenv.TemplateReplacement with
+ | Some (tcref2, ilty, _) when tyconRefEq g tcref tcref2 -> ilty
+ | _ ->
let tinst = DropErasedTyargs tinst
-
// See above note on ptrsOK
if ptrsOK = PtrTypesOK && tyconRefEq g tcref g.nativeptr_tcr && (freeInTypes CollectTypars tinst).FreeTypars.IsEmpty then
GenNamedTyAppAux amap m tyenv ptrsOK g.ilsigptr_tcr tinst
@@ -567,7 +581,7 @@ and GenUnionRef (amap: ImportMap) m (tcref: TyconRef) =
| ValueNone -> failwith "GenUnionRef m"
| ValueSome funion ->
cached funion.CompiledRepresentation (fun () ->
- let tyenvinner = TypeReprEnv.ForTycon tycon
+ let tyenvinner = TypeReprEnv.Empty.ForTycon tycon
match tcref.CompiledRepresentation with
| CompiledTypeRepr.ILAsmOpen _ -> failwith "GenUnionRef m: unexpected ASM tyrep"
| CompiledTypeRepr.ILAsmNamed (tref, _, _) ->
@@ -665,9 +679,18 @@ let GenFieldSpecForStaticField (isInteractive, g, ilContainerTy, vspec: Val, nm,
let ilFieldContainerTy = mkILTyForCompLoc (CompLocForInitClass cloc)
mkILFieldSpecInTy (ilFieldContainerTy, fieldName, ilTy)
-let GenRecdFieldRef m cenv tyenv (rfref: RecdFieldRef) tyargs =
- let tyenvinner = TypeReprEnv.ForTycon rfref.Tycon
- mkILFieldSpecInTy(GenTyApp cenv.amap m tyenv rfref.TyconRef.CompiledRepresentation tyargs,
+
+let GenRecdFieldRef m cenv (tyenv: TypeReprEnv) (rfref: RecdFieldRef) tyargs =
+ // Fixup references to the fields of a struct machine template
+ match tyenv.TemplateReplacement with
+ | Some (tcref2, ilty, inst) when tyconRefEq cenv.g rfref.TyconRef tcref2 ->
+ mkILFieldSpecInTy(ilty,
+ ComputeFieldName rfref.Tycon rfref.RecdField,
+ GenType cenv.amap m tyenv (instType inst rfref.RecdField.FormalType))
+ | _ ->
+ let tyenvinner = TypeReprEnv.Empty.ForTycon rfref.Tycon
+ let ilty = GenTyApp cenv.amap m tyenv rfref.TyconRef.CompiledRepresentation tyargs
+ mkILFieldSpecInTy(ilty,
ComputeFieldName rfref.Tycon rfref.RecdField,
GenType cenv.amap m tyenvinner rfref.RecdField.FormalType)
@@ -788,9 +811,6 @@ type IlxClosureInfo =
/// The ILX specification for the closure
cloSpec: IlxClosureSpec
- /// The attributes that get attached to the closure class
- cloAttribs: Attribs
-
/// The generic parameters for the closure, i.e. the type variables it captures
cloILGenericParams: IL.ILGenericParameterDefs
@@ -895,13 +915,16 @@ and Mark =
and sequel =
| EndFilter
- /// Exit a 'handler' block
- /// The integer says which local to save result in
- | LeaveHandler of (bool (* finally? *) * int * Mark)
+ /// Exit a 'handler' block. The integer says which local to save result in
+ | LeaveHandler of
+ isFinally: bool *
+ whereToSaveOpt: (int * ILType) option *
+ afterHandler: Mark *
+ hasResult: bool
/// Branch to the given mark
| Br of Mark
-
+
/// Execute the given comparison-then-branch instructions on the result of the expression
/// If the branch isn't taken then drop through.
| CmpThenBrOrContinue of Pops * ILInstr list
@@ -939,6 +962,10 @@ and IlxGenEnv =
/// Indicates the default "place" for stuff we're currently generating
cloc: CompileLocation
+ /// The sequel to use for an "early exit" in a state machine, e.g. a return from the middle of an
+ /// async block
+ exitSequel: sequel
+
/// Hiding information down the signature chain, used to compute what's public to the assembly
sigToImplRemapInfo: (Remap * SignatureHidingInfo) list
@@ -981,9 +1008,9 @@ let SetIsInLoop isInLoop eenv =
if eenv.isInLoop = isInLoop then eenv
else { eenv with isInLoop = isInLoop }
-let ReplaceTyenv tyenv (eenv: IlxGenEnv) = {eenv with tyenv = tyenv }
+let EnvForTypars tps eenv = {eenv with tyenv = eenv.tyenv.ForTypars tps }
-let EnvForTypars tps eenv = {eenv with tyenv = TypeReprEnv.ForTypars tps }
+let EnvForTycon tps eenv = {eenv with tyenv = eenv.tyenv.ForTycon tps }
let AddTyparsToEnv typars (eenv: IlxGenEnv) = {eenv with tyenv = eenv.tyenv.Add typars}
@@ -1032,6 +1059,9 @@ let AddStorageForVal (g: TcGlobals) (v, s) eenv =
let AddStorageForLocalVals g vals eenv =
List.foldBack (fun (v, s) acc -> AddStorageForVal g (v, notlazy s) acc) vals eenv
+let AddTemplateReplacement eenv (tcref, ilty, inst) =
+ { eenv with tyenv = eenv.tyenv.WithTemplateReplacement (tcref, ilty, inst) }
+
let AddStorageForLocalWitness eenv (w,s) =
{ eenv with witnessesInScope = eenv.witnessesInScope.SetItem (w, s) }
@@ -1072,7 +1102,7 @@ let GetMethodSpecForMemberVal amap g (memberInfo: ValMemberInfo) (vref: ValRef)
let tps, witnessInfos, curriedArgInfos, returnTy, retInfo =
assert(vref.ValReprInfo.IsSome)
GetTopValTypeInCompiledForm g vref.ValReprInfo.Value numEnclosingTypars vref.Type m
- let tyenvUnderTypars = TypeReprEnv.ForTypars tps
+ let tyenvUnderTypars = TypeReprEnv.Empty.ForTypars tps
let flatArgInfos = List.concat curriedArgInfos
let isCtor = (memberInfo.MemberFlags.MemberKind = SynMemberKind.Constructor)
let cctor = (memberInfo.MemberFlags.MemberKind = SynMemberKind.ClassConstructor)
@@ -1189,7 +1219,7 @@ let ComputeStorageForFSharpFunctionOrFSharpExtensionMember amap (g: TcGlobals) c
let nm = vref.CompiledName g.CompilerGlobalState
let numEnclosingTypars = CountEnclosingTyparsOfActualParentOfVal vref.Deref
let (tps, witnessInfos, curriedArgInfos, returnTy, retInfo) = GetTopValTypeInCompiledForm g topValInfo numEnclosingTypars vref.Type m
- let tyenvUnderTypars = TypeReprEnv.ForTypars tps
+ let tyenvUnderTypars = TypeReprEnv.Empty.ForTypars tps
let (methodArgTys, paramInfos) = curriedArgInfos |> List.concat |> List.unzip
let ilMethodArgTys = GenParamTypes amap m tyenvUnderTypars false methodArgTys
let ilRetTy = GenReturnType amap m tyenvUnderTypars returnTy
@@ -1235,7 +1265,7 @@ let ComputeStorageForTopVal (amap, g, optIntraAssemblyInfo: IlxGenIntraAssemblyI
if vref.Deref.IsCompiledAsStaticPropertyWithoutField then
let nm = "get_"+nm
- let tyenvUnderTypars = TypeReprEnv.ForTypars []
+ let tyenvUnderTypars = TypeReprEnv.Empty.ForTypars []
let ilRetTy = GenType amap m tyenvUnderTypars vref.Type
let ty = mkILTyForCompLoc cloc
let mspec = mkILStaticMethSpecInTy (ty, nm, [], ilRetTy, [])
@@ -1656,7 +1686,7 @@ type AssemblyBuilder(cenv: cenv, anonTypeTable: AnonTypeGenerationTable) as mgbu
let ilTypeDefAttribs = mkILCustomAttrs [ g.CompilerGeneratedAttribute; mkCompilationMappingAttr g (int SourceConstructFlags.RecordType) ]
- let ilInterfaceTys = [ for (ity, _, _) in tcaug.tcaug_interfaces -> GenType cenv.amap m (TypeReprEnv.ForTypars tps) ity ]
+ let ilInterfaceTys = [ for (ity, _, _) in tcaug.tcaug_interfaces -> GenType cenv.amap m (TypeReprEnv.Empty.ForTypars tps) ity ]
let ilTypeDef =
mkILGenericClass (ilTypeRef.Name, ILTypeDefAccess.Public, ilGenericParams, ilBaseTy, ilInterfaceTys,
@@ -2136,7 +2166,8 @@ let LocalScope nm cgbuf (f: (Mark * Mark) -> 'a) : 'a =
CG.SetMarkToHere cgbuf endScope
res
-let compileSequenceExpressions = true // try (System.Environment.GetEnvironmentVariable("COMPILED_SEQ") <> null) with _ -> false
+let compileSequenceExpressions = true // try (System.Environment.GetEnvironmentVariable("FSHARP_COMPILED_SEQ") <> null) with _ -> false
+let compileStateMachineExpressions = true // try (System.Environment.GetEnvironmentVariable("FSHARP_COMPILED_STATEMACHINES") <> null) with _ -> false
//-------------------------------------------------------------------------
// Sequence Point Logic
@@ -2206,11 +2237,18 @@ let rec FirstEmittedCodeWillBeDebugPoint g sp expr =
| Expr.LetRec (binds, body, _, _) ->
binds |> List.exists (BindingEmitsDebugPoint g) ||
(binds |> List.forall (BindingEmitsNoCode g) && FirstEmittedCodeWillBeDebugPoint g sp body)
- | Expr.Sequential (_, _, NormalSeq, spSeq, _) ->
+ | Expr.Sequential (stmt1, expr2, NormalSeq, spSeq, _) ->
+ match spSeq with
+ | DebugPointAtSequential.SuppressNeither -> FirstEmittedCodeWillBeDebugPoint g sp stmt1
+ | DebugPointAtSequential.SuppressExpr -> FirstEmittedCodeWillBeDebugPoint g sp stmt1
+ | DebugPointAtSequential.SuppressStmt -> FirstEmittedCodeWillBeDebugPoint g sp expr2
+ | DebugPointAtSequential.SuppressBoth -> false
+ | Expr.Sequential (expr1, stmt2, ThenDoSeq, spSeq, _) ->
match spSeq with
- | DebugPointAtSequential.Both -> true
- | DebugPointAtSequential.StmtOnly -> true
- | DebugPointAtSequential.ExprOnly -> false
+ | DebugPointAtSequential.SuppressNeither -> FirstEmittedCodeWillBeDebugPoint g sp expr1
+ | DebugPointAtSequential.SuppressExpr -> FirstEmittedCodeWillBeDebugPoint g sp stmt2
+ | DebugPointAtSequential.SuppressStmt -> FirstEmittedCodeWillBeDebugPoint g sp expr1
+ | DebugPointAtSequential.SuppressBoth -> false
| Expr.Match (DebugPointAtBinding.Yes _, _, _, _, _, _) -> true
| Expr.Op ((TOp.TryWith (DebugPointAtTry.Yes _, _)
| TOp.TryFinally (DebugPointAtTry.Yes _, _)
@@ -2370,7 +2408,29 @@ and GenExprPreSteps (cenv: cenv) (cgbuf: CodeGenBuffer) eenv sp expr sequel =
GenSequenceExpr cenv cgbuf eenv info sequel
true
| None ->
- false
+
+ match LowerStateMachineExpr cenv.g expr with
+ | LoweredStateMachineResult.Lowered res ->
+ checkLanguageFeatureError cenv.g.langVersion LanguageFeature.ResumableStateMachines expr.Range
+ GenStructStateMachine cenv cgbuf eenv res sequel
+ true
+ | LoweredStateMachineResult.UseAlternative (msg, altExpr) ->
+ checkLanguageFeatureError cenv.g.langVersion LanguageFeature.ResumableStateMachines expr.Range
+ warning(Error(FSComp.SR.reprStateMachineNotCompilable(msg), expr.Range))
+ GenExpr cenv cgbuf eenv sp altExpr sequel
+ true
+ | LoweredStateMachineResult.NoAlternative msg ->
+ checkLanguageFeatureError cenv.g.langVersion LanguageFeature.ResumableStateMachines expr.Range
+ errorR(Error(FSComp.SR.reprStateMachineNotCompilableNoAlternative(msg), expr.Range))
+ GenDefaultValue cenv cgbuf eenv (tyOfExpr cenv.g expr, expr.Range)
+ true
+ | LoweredStateMachineResult.NotAStateMachine ->
+ match expr with
+ | IfUseResumableStateMachinesExpr g (_thenExpr, elseExpr) ->
+ GenExpr cenv cgbuf eenv sp elseExpr sequel
+ true
+ | _ ->
+ false
and GenExprAux (cenv: cenv) (cgbuf: CodeGenBuffer) eenv sp expr sequel =
let g = cenv.g
@@ -2497,7 +2557,7 @@ and GenExprAux (cenv: cenv) (cgbuf: CodeGenBuffer) eenv sp expr sequel =
CG.EmitInstr cgbuf (pop 0) Push0 (I_br label)
// NOTE: discard sequel
| TOp.Return, [e], _ ->
- GenExpr cenv cgbuf eenv SPSuppress e Return
+ GenExpr cenv cgbuf eenv SPSuppress e eenv.exitSequel
// NOTE: discard sequel
| TOp.Return, [], _ ->
GenSequel cenv eenv.cloc cgbuf ReturnVoid
@@ -2531,6 +2591,7 @@ and GenExprs cenv cgbuf eenv es =
List.iter (fun e -> GenExpr cenv cgbuf eenv SPSuppress e Continue) es
and CodeGenMethodForExpr cenv mgbuf (spReq, entryPointInfo, methodName, eenv, alreadyUsedArgs, expr0, sequel0) =
+ let eenv = { eenv with exitSequel = sequel0 }
let _, code =
CodeGenMethod cenv mgbuf (entryPointInfo, methodName, eenv, alreadyUsedArgs,
(fun cgbuf eenv -> GenExpr cenv cgbuf eenv spReq expr0 sequel0),
@@ -2541,9 +2602,17 @@ and CodeGenMethodForExpr cenv mgbuf (spReq, entryPointInfo, methodName, eenv, al
// Generate sequels
//--------------------------------------------------------------------------
-(* does the sequel discard its result, and if so what does it do next? *)
+/// Adjust the sequel for an implicit discard (e.g. a discard that occurs by
+/// not generating a 'unit' expression at all)
and sequelAfterDiscard sequel =
match sequel with
+ | LeaveHandler (isFinally, whereToSaveResultOpt, afterHandler, true) ->
+ // If we're not saving the result as we leave a handler and we're doing a discard
+ // then we can just adjust the sequel to record the fact we've implicitly done a discard
+ if isFinally || whereToSaveResultOpt.IsNone then
+ Some (LeaveHandler (isFinally, whereToSaveResultOpt, afterHandler, false))
+ else
+ None
| DiscardThen sequel -> Some sequel
| EndLocalScope(sq, mark) -> sequelAfterDiscard sq |> Option.map (fun sq -> EndLocalScope(sq, mark))
| _ -> None
@@ -2599,12 +2668,17 @@ and GenSequel cenv cloc cgbuf sequel =
cgbuf.EmitStartOfHiddenCode()
CG.EmitInstr cgbuf (pop 0) Push0 AI_nop
CG.EmitInstr cgbuf (pop 0) Push0 (I_br x.CodeLabel)
- | LeaveHandler (isFinally, whereToSaveResult, x) ->
- if isFinally then
- CG.EmitInstr cgbuf (pop 1) Push0 AI_pop
- else
- EmitSetLocal cgbuf whereToSaveResult
- CG.EmitInstr cgbuf (pop 0) Push0 (if isFinally then I_endfinally else I_leave(x.CodeLabel))
+ | LeaveHandler (isFinally, whereToSaveResultOpt, afterHandler, hasResult) ->
+ if hasResult then
+ if isFinally then
+ CG.EmitInstr cgbuf (pop 1) Push0 AI_pop
+ else
+ match whereToSaveResultOpt with
+ | None ->
+ CG.EmitInstr cgbuf (pop 1) Push0 AI_pop
+ | Some (whereToSaveResult, _) ->
+ EmitSetLocal cgbuf whereToSaveResult
+ CG.EmitInstr cgbuf (pop 0) Push0 (if isFinally then I_endfinally else I_leave(afterHandler.CodeLabel))
| EndFilter ->
CG.EmitInstr cgbuf (pop 1) Push0 I_endfilter
)
@@ -2747,19 +2821,27 @@ and GenLinearExpr cenv cgbuf eenv sp expr sequel preSteps (contf: FakeUnit -> Fa
// Compiler generated sequential executions result in suppressions of sequence points on both
// left and right of the sequence
- let spAction, spExpr =
+ let spStmt, spExpr =
(match spSeq with
- | DebugPointAtSequential.Both -> SPAlways, SPAlways
- | DebugPointAtSequential.StmtOnly -> SPSuppress, sp
- | DebugPointAtSequential.ExprOnly -> sp, SPSuppress)
+ | DebugPointAtSequential.SuppressNeither -> SPAlways, SPAlways
+ | DebugPointAtSequential.SuppressStmt -> SPSuppress, sp
+ | DebugPointAtSequential.SuppressExpr -> sp, SPSuppress
+ | DebugPointAtSequential.SuppressBoth -> SPSuppress, SPSuppress)
match specialSeqFlag with
| NormalSeq ->
- GenExpr cenv cgbuf eenv spAction e1 discard
+ GenExpr cenv cgbuf eenv spStmt e1 discard
GenLinearExpr cenv cgbuf eenv spExpr e2 sequel true contf
| ThenDoSeq ->
- GenExpr cenv cgbuf eenv spExpr e1 Continue
- GenExpr cenv cgbuf eenv spAction e2 discard
- GenSequel cenv eenv.cloc cgbuf sequel
+ let g = cenv.g
+ let isUnit = isUnitTy g (tyOfExpr g e1)
+ if isUnit then
+ GenExpr cenv cgbuf eenv spExpr e1 discard
+ GenExpr cenv cgbuf eenv spStmt e2 discard
+ GenUnitThenSequel cenv eenv e2.Range eenv.cloc cgbuf sequel
+ else
+ GenExpr cenv cgbuf eenv spExpr e1 Continue
+ GenExpr cenv cgbuf eenv spStmt e2 discard
+ GenSequel cenv eenv.cloc cgbuf sequel
contf Fake
| Expr.Let (bind, body, _, _) ->
@@ -2771,7 +2853,7 @@ and GenLinearExpr cenv cgbuf eenv sp expr sequel preSteps (contf: FakeUnit -> Fa
let startScope, endScope as scopeMarks = StartDelayedLocalScope "let" cgbuf
let eenv = AllocStorageForBind cenv cgbuf scopeMarks eenv bind
let spBind = GenDebugPointForBind cenv cgbuf bind
- GenBindingAfterDebugPoint cenv cgbuf eenv spBind bind (Some startScope)
+ GenBindingAfterDebugPoint cenv cgbuf eenv spBind bind false (Some startScope)
// Work out if we need a sequence point for the body. For any "user" binding then the body gets SPAlways.
// For invisible compiler-generated bindings we just use "sp", unless its body is another invisible binding
@@ -2883,7 +2965,7 @@ and GenAllocRecd cenv cgbuf eenv ctorInfo (tcref,argtys,args,m) sequel =
| RecdExpr ->
GenExprs cenv cgbuf eenv args
// generate a reference to the record constructor
- let tyenvinner = TypeReprEnv.ForTyconRef tcref
+ let tyenvinner = eenv.tyenv.ForTyconRef tcref
CG.EmitInstr cgbuf (pop args.Length) (Push [ty])
(mkNormalNewobj
(mkILCtorMethSpecForTy (ty, relevantFields |> List.map (fun f -> GenType cenv.amap m tyenvinner f.FormalType) )))
@@ -3197,7 +3279,7 @@ and GenUntupledArgExpr cenv cgbuf eenv m argInfos expr =
let bind = mkCompGenBind locv expr
LocalScope "untuple" cgbuf (fun scopeMarks ->
let eenvinner = AllocStorageForBind cenv cgbuf scopeMarks eenv bind
- GenBinding cenv cgbuf eenvinner bind
+ GenBinding cenv cgbuf eenvinner bind false
let tys = destRefTupleTy g ty
assert (tys.Length = numRequiredExprs)
argInfos |> List.iteri (fun i _ -> GenGetTupleField cenv cgbuf eenvinner (tupInfoRef, loce, tys, i, m) Continue)
@@ -3348,6 +3430,15 @@ and GenApp (cenv: cenv) cgbuf eenv (f, fty, tyargs, curriedArgs, m) sequel =
CG.EmitInstr cgbuf (pop 2) (Push [g.ilg.typ_Bool]) AI_ceq
GenSequel cenv eenv.cloc cgbuf sequel
+ | Expr.Val (v, _, m), _, _
+ when valRefEq g v g.cgh__resumeAt_vref ||
+ valRefEq g v g.cgh__resumableEntry_vref ||
+ valRefEq g v g.cgh__stateMachine_vref
+ ->
+ errorR(Error(FSComp.SR.ilxgenInvalidConstructInStateMachineDuringCodegen(v.DisplayName), m))
+ CG.EmitInstr cgbuf (pop 0) (Push [g.ilg.typ_Object]) AI_ldnull
+ GenSequel cenv eenv.cloc cgbuf sequel
+
// Emit "methodhandleof" calls as ldtoken instructions
//
// The token for the "GenericMethodDefinition" is loaded
@@ -3702,7 +3793,8 @@ and GenIndirectCall cenv cgbuf eenv (functy, tyargs, curriedArgs, m) sequel =
// Generate try expressions
//--------------------------------------------------------------------------
-and GenTry cenv cgbuf eenv scopeMarks (e1, m, resty, spTry) =
+and GenTry cenv cgbuf eenv scopeMarks (e1, m, resultTy, spTry) =
+ let g = cenv.g
let sp =
match spTry with
| DebugPointAtTry.Yes m -> CG.EmitSeqPoint cgbuf m; SPAlways
@@ -3713,22 +3805,33 @@ and GenTry cenv cgbuf eenv scopeMarks (e1, m, resty, spTry) =
let startTryMark = CG.GenerateMark cgbuf "startTryMark"
let endTryMark = CG.GenerateDelayMark cgbuf "endTryMark"
let afterHandler = CG.GenerateDelayMark cgbuf "afterHandler"
- let eenvinner = {eenvinner with withinSEH = true}
- let ilResultTy = GenType cenv.amap m eenvinner.tyenv resty
+ let ilResultTyOpt =
+ if isUnitTy g resultTy then
+ None
+ else
+ Some (GenType cenv.amap m eenvinner.tyenv resultTy)
+
+ let whereToSaveOpt, eenvinner =
+ match ilResultTyOpt with
+ | None -> None, eenvinner
+ | Some ilResultTy ->
+ // Ensure that we have an g.CompilerGlobalState
+ assert(cenv.g.CompilerGlobalState |> Option.isSome)
+ let whereToSave, _realloc, eenvinner =
+ AllocLocal cenv cgbuf eenvinner true (cenv.g.CompilerGlobalState.Value.IlxGenNiceNameGenerator.FreshCompilerGeneratedName ("tryres", m), ilResultTy, false) (startTryMark, endTryMark)
+ Some (whereToSave, ilResultTy), eenvinner
- let whereToSave, _realloc, eenvinner =
- // Ensure that we have an g.CompilerGlobalState
- assert(cenv.g.CompilerGlobalState |> Option.isSome)
- AllocLocal cenv cgbuf eenvinner true (cenv.g.CompilerGlobalState.Value.IlxGenNiceNameGenerator.FreshCompilerGeneratedName ("tryres", m), ilResultTy, false) (startTryMark, endTryMark)
+ let exitSequel = LeaveHandler (false, whereToSaveOpt, afterHandler, true)
+ let eenvinner = {eenvinner with withinSEH = true; exitSequel = exitSequel}
// Generate the body of the try. In the normal case (DebugPointAtTry.Yes) we generate a sequence point
// both on the 'try' keyword and on the start of the expression in the 'try'. For inlined code and
// compiler generated 'try' blocks (i.e. DebugPointAtTry.No, used for the try/finally implicit
// in a 'use' or 'foreach'), we suppress the sequence point
- GenExpr cenv cgbuf eenvinner sp e1 (LeaveHandler (false, whereToSave, afterHandler))
+ GenExpr cenv cgbuf eenvinner sp e1 exitSequel
CG.SetMarkToHere cgbuf endTryMark
let tryMarks = (startTryMark.CodeLabel, endTryMark.CodeLabel)
- whereToSave, eenvinner, stack, tryMarks, afterHandler, ilResultTy
+ whereToSaveOpt, eenvinner, stack, tryMarks, afterHandler
and GenTryWith cenv cgbuf eenv (e1, vf: Val, ef, vh: Val, eh, m, resty, spTry, spWith) sequel =
let g = cenv.g
@@ -3736,7 +3839,7 @@ and GenTryWith cenv cgbuf eenv (e1, vf: Val, ef, vh: Val, eh, m, resty, spTry, s
// Save the stack - gross because IL flushes the stack at the exn. handler
// note: eenvinner notes spill vars are live
LocalScope "trystack" cgbuf (fun scopeMarks ->
- let whereToSave, eenvinner, stack, tryMarks, afterHandler, ilResultTy = GenTry cenv cgbuf eenv scopeMarks (e1, m, resty, spTry)
+ let whereToSaveOpt, eenvinner, stack, tryMarks, afterHandler = GenTry cenv cgbuf eenv scopeMarks (e1, m, resty, spTry)
// Now the filter and catch blocks
@@ -3745,6 +3848,7 @@ and GenTryWith cenv cgbuf eenv (e1, vf: Val, ef, vh: Val, eh, m, resty, spTry, s
let startOfFilter = CG.GenerateMark cgbuf "startOfFilter"
let afterFilter = CG.GenerateDelayMark cgbuf "afterFilter"
let (sequelOnBranches, afterJoin, stackAfterJoin, sequelAfterJoin) = GenJoinPoint cenv cgbuf "filter" eenv g.int_ty m EndFilter
+ let eenvinner = { eenvinner with exitSequel = sequelOnBranches }
// We emit the sequence point for the 'with' keyword span on the start of the filter
// block. However the targets of the filter block pattern matching should not get any
// sequence points (they will be 'true'/'false' values indicating if the exception has been
@@ -3782,7 +3886,8 @@ and GenTryWith cenv cgbuf eenv (e1, vf: Val, ef, vh: Val, eh, m, resty, spTry, s
CG.EmitInstr cgbuf (pop 1) (Push [g.iltyp_Exception]) (I_castclass g.iltyp_Exception)
GenStoreVal cenv cgbuf eenvinner vh.Range vh
- GenExpr cenv cgbuf eenvinner SPAlways eh (LeaveHandler (false, whereToSave, afterHandler))
+ let exitSequel = LeaveHandler (false, whereToSaveOpt, afterHandler, true)
+ GenExpr cenv cgbuf eenvinner SPAlways eh exitSequel
let endOfHandler = CG.GenerateMark cgbuf "endOfHandler"
let handlerMarks = (startOfHandler.CodeLabel, endOfHandler.CodeLabel)
@@ -3800,8 +3905,10 @@ and GenTryWith cenv cgbuf eenv (e1, vf: Val, ef, vh: Val, eh, m, resty, spTry, s
GenStoreVal cenv cgbuf eenvinner m vh
- GenExpr cenv cgbuf eenvinner SPAlways eh (LeaveHandler (false, whereToSave, afterHandler))
-
+ let exitSequel = LeaveHandler (false, whereToSaveOpt, afterHandler, true)
+ let eenvinner = { eenvinner with exitSequel = exitSequel }
+ GenExpr cenv cgbuf eenvinner SPAlways eh exitSequel
+
let endOfHandler = CG.GenerateMark cgbuf "endOfHandler"
let handlerMarks = (startOfHandler.CodeLabel, endOfHandler.CodeLabel)
ILExceptionClause.TypeCatch(g.ilg.typ_Object, handlerMarks)
@@ -3818,8 +3925,12 @@ and GenTryWith cenv cgbuf eenv (e1, vf: Val, ef, vh: Val, eh, m, resty, spTry, s
(* Restore the stack and load the result *)
EmitRestoreStack cgbuf stack (* RESTORE *)
- EmitGetLocal cgbuf ilResultTy whereToSave
- GenSequel cenv eenv.cloc cgbuf sequel
+ match whereToSaveOpt with
+ | Some (whereToSave, ilResultTy) ->
+ EmitGetLocal cgbuf ilResultTy whereToSave
+ GenSequel cenv eenv.cloc cgbuf sequel
+ | None ->
+ GenUnitThenSequel cenv eenv m eenv.cloc cgbuf sequel
)
@@ -3828,7 +3939,7 @@ and GenTryFinally cenv cgbuf eenv (bodyExpr, handlerExpr, m, resty, spTry, spFin
// note: eenvinner notes spill vars are live
LocalScope "trystack" cgbuf (fun scopeMarks ->
- let whereToSave, eenvinner, stack, tryMarks, afterHandler, ilResultTy = GenTry cenv cgbuf eenv scopeMarks (bodyExpr, m, resty, spTry)
+ let whereToSaveOpt, eenvinner, stack, tryMarks, afterHandler = GenTry cenv cgbuf eenv scopeMarks (bodyExpr, m, resty, spTry)
// Now the catch/finally block
let startOfHandler = CG.GenerateMark cgbuf "startOfHandler"
@@ -3840,7 +3951,8 @@ and GenTryFinally cenv cgbuf eenv (bodyExpr, handlerExpr, m, resty, spTry, spFin
| DebugPointAtFinally.Body -> SPAlways
| DebugPointAtFinally.No -> SPSuppress
- GenExpr cenv cgbuf eenvinner sp handlerExpr (LeaveHandler (true, whereToSave, afterHandler))
+ let exitSequel = LeaveHandler (true, whereToSaveOpt, afterHandler, true)
+ GenExpr cenv cgbuf eenvinner sp handlerExpr exitSequel
let endOfHandler = CG.GenerateMark cgbuf "endOfHandler"
let handlerMarks = (startOfHandler.CodeLabel, endOfHandler.CodeLabel)
cgbuf.EmitExceptionClause
@@ -3853,8 +3965,12 @@ and GenTryFinally cenv cgbuf eenv (bodyExpr, handlerExpr, m, resty, spTry, spFin
// Restore the stack and load the result
cgbuf.EmitStartOfHiddenCode()
EmitRestoreStack cgbuf stack
- EmitGetLocal cgbuf ilResultTy whereToSave
- GenSequel cenv eenv.cloc cgbuf sequel
+ match whereToSaveOpt with
+ | Some (whereToSave, ilResultTy) ->
+ EmitGetLocal cgbuf ilResultTy whereToSave
+ GenSequel cenv eenv.cloc cgbuf sequel
+ | None ->
+ GenUnitThenSequel cenv eenv m eenv.cloc cgbuf sequel
)
//--------------------------------------------------------------------------
@@ -3954,7 +4070,7 @@ and GenForLoop cenv cgbuf eenv (spFor, v, e1, dir, e2, loopBody, m) sequel =
// Generate while-loop
//--------------------------------------------------------------------------
-and GenWhileLoop cenv cgbuf eenv (spWhile, e1, e2, m) sequel =
+and GenWhileLoop cenv cgbuf eenv (spWhile, condExpr, bodyExpr, m) sequel =
let eenv = SetIsInLoop true eenv
let finish = CG.GenerateDelayMark cgbuf "while_finish"
let startTest = CG.GenerateMark cgbuf "startTest"
@@ -3965,9 +4081,9 @@ and GenWhileLoop cenv cgbuf eenv (spWhile, e1, e2, m) sequel =
| DebugPointAtWhile.No -> SPSuppress
// SEQUENCE POINTS: Emit a sequence point to cover all of 'while e do'
- GenExpr cenv cgbuf eenv spCondition e1 (CmpThenBrOrContinue (pop 1, [ I_brcmp(BI_brfalse, finish.CodeLabel) ]))
+ GenExpr cenv cgbuf eenv spCondition condExpr (CmpThenBrOrContinue (pop 1, [ I_brcmp(BI_brfalse, finish.CodeLabel) ]))
- GenExpr cenv cgbuf eenv SPAlways e2 (DiscardThen (Br startTest))
+ GenExpr cenv cgbuf eenv SPAlways bodyExpr (DiscardThen (Br startTest))
CG.SetMarkToHere cgbuf finish
// SEQUENCE POINTS: Emit a sequence point to cover 'done' if present
@@ -4440,17 +4556,26 @@ and GenFormalSlotsig m cenv eenv (TSlotSig(_, ty, ctps, mtps, paraml, returnTy))
let ilTy = GenType cenv.amap m eenv.tyenv ty
let eenvForSlotSig = EnvForTypars (ctps @ mtps) eenv
let ilParams = paraml |> List.map (GenSlotParam m cenv eenvForSlotSig)
- let ilRetTy = GenReturnType cenv.amap m eenvForSlotSig.tyenv returnTy
- let ilRet = mkILReturn ilRetTy
- let ilRet =
- match returnTy with
- | None -> ilRet
- | Some ty ->
- match GenReadOnlyAttributeIfNecessary cenv.g ty with
- | Some attr -> ilRet.WithCustomAttrs (mkILCustomAttrs (ilRet.CustomAttrs.AsList @ [attr]))
- | None -> ilRet
+ let ilRet = GenFormalReturnType m cenv eenvForSlotSig returnTy
ilTy, ilParams, ilRet
+and GenOverridesSpec cenv eenv slotsig m =
+ let (TSlotSig(nameOfOverridenMethod, _, _, methodTypars, _, _)) = slotsig
+ let ilOverrideTy, ilOverrideParams, ilOverrideRet = GenFormalSlotsig m cenv eenv slotsig
+ let ilOverrideTyRef = ilOverrideTy.TypeRef
+ let ilOverrideMethRef = mkILMethRef(ilOverrideTyRef, ILCallingConv.Instance, nameOfOverridenMethod, List.length (DropErasedTypars methodTypars), typesOfILParams ilOverrideParams, ilOverrideRet.Type)
+ OverridesSpec(ilOverrideMethRef, ilOverrideTy)
+
+and GenFormalReturnType m cenv eenvFormal returnTy : ILReturn =
+ let ilRetTy = GenReturnType cenv.amap m eenvFormal.tyenv returnTy
+ let ilRet = mkILReturn ilRetTy
+ match returnTy with
+ | None -> ilRet
+ | Some ty ->
+ match GenReadOnlyAttributeIfNecessary cenv.g ty with
+ | Some attr -> ilRet.WithCustomAttrs (mkILCustomAttrs (ilRet.CustomAttrs.AsList @ [attr]))
+ | None -> ilRet
+
and instSlotParam inst (TSlotParam(nm, ty, inFlag, fl2, fl3, attrs)) =
TSlotParam(nm, instType inst ty, inFlag, fl2, fl3, attrs)
@@ -4476,20 +4601,18 @@ and GenNameOfOverridingMethod cenv (useMethodImpl, slotsig) =
else
nameOfOverridenMethod
-and GenMethodImpl cenv eenv (useMethodImpl, (TSlotSig(nameOfOverridenMethod, _, _, _, _, _) as slotsig)) m =
- let ilOverrideTy, ilOverrideParams, ilOverrideRet = GenFormalSlotsig m cenv eenv slotsig
+and GenMethodImpl cenv eenv (useMethodImpl, slotsig) m =
+ let ilOverridesSpec = GenOverridesSpec cenv eenv slotsig m
let nameOfOverridingMethod = GenNameOfOverridingMethod cenv (useMethodImpl, slotsig)
nameOfOverridingMethod,
(fun (ilTyForOverriding, methTyparsOfOverridingMethod) ->
- let ilOverrideTyRef = ilOverrideTy.TypeRef
- let ilOverrideMethRef = mkILMethRef(ilOverrideTyRef, ILCallingConv.Instance, nameOfOverridenMethod, List.length (DropErasedTypars methTyparsOfOverridingMethod), typesOfILParams ilOverrideParams, ilOverrideRet.Type)
let eenvForOverrideBy = AddTyparsToEnv methTyparsOfOverridingMethod eenv
let ilParamsOfOverridingMethod, ilReturnOfOverridingMethod = GenActualSlotsig m cenv eenvForOverrideBy slotsig methTyparsOfOverridingMethod []
let ilOverrideMethGenericParams = GenGenericParams cenv eenvForOverrideBy methTyparsOfOverridingMethod
let ilOverrideMethGenericArgs = mkILFormalGenericArgs 0 ilOverrideMethGenericParams
let ilOverrideBy = mkILInstanceMethSpecInTy(ilTyForOverriding, nameOfOverridingMethod, typesOfILParams ilParamsOfOverridingMethod, ilReturnOfOverridingMethod.Type, ilOverrideMethGenericArgs)
- { Overrides = OverridesSpec(ilOverrideMethRef, ilOverrideTy)
+ { Overrides = ilOverridesSpec
OverrideBy = ilOverrideBy })
and bindBaseOrThisVarOpt cenv eenv baseValOpt =
@@ -4544,19 +4667,220 @@ and GenObjectMethod cenv eenvinner (cgbuf: CodeGenBuffer) useMethodImpl tmethod
let mdef = mdef.With(customAttrs = mkILCustomAttrs ilAttribs)
[(useMethodImpl, methodImplGenerator, methTyparsOfOverridingMethod), mdef]
-and GenObjectExpr cenv cgbuf eenvouter expr (baseType, baseValOpt, basecall, overrides, interfaceImpls, m) sequel =
+and GenStructStateMachine cenv cgbuf eenvouter (res: LoweredStateMachine) sequel =
+
+ let (LoweredStateMachine
+ (templateStructTy, dataTy, stateVars, thisVars,
+ (moveNextThisVar, moveNextBody),
+ (setStateMachineThisVar, setStateMachineStateVar, setStateMachineBody),
+ (afterCodeThisVar, afterCodeBody))) = res
+ let m = moveNextBody.Range
let g = cenv.g
- let cloinfo, _, eenvinner = GetIlxClosureInfo cenv m false false [] eenvouter expr
+ let amap = cenv.amap
+
+ let stateVarsSet = stateVars |> List.map (fun vref -> vref.Deref) |> Zset.ofList valOrder
+
+ // Find the free variables of the closure, to make them further fields of the object.
+ let cloinfo, _, eenvinner =
+ // State vars are only populated for state machine objects
+ //
+ // Like in GenSequenceExpression we pretend any stateVars and the stateMachineVar are bound in the outer environment. This prevents the being
+ // considered true free variables that need to be passed to the constructor.
+ //
+ // Note, the 'let' bindings for the stateVars have already been transformed to 'set' expressions, and thus the stateVars are now
+ // free variables of the expression.
+ let eenvouter = eenvouter |> AddStorageForLocalVals g (stateVars |> List.map (fun v -> v.Deref, Local(0, false, None)))
+ let eenvouter = eenvouter |> AddStorageForLocalVals g (thisVars |> List.map (fun v -> v.Deref, Local(0, false, None)))
+ let eenvouter = eenvouter |> AddStorageForLocalVals g [ (moveNextThisVar, Local(0, false, None)) ]
+ GetIlxClosureInfo cenv m ILBoxity.AsValue false false (mkLocalValRef moveNextThisVar :: thisVars) eenvouter moveNextBody
+
+ let cloFreeVars = cloinfo.cloFreeVars
+
+ let ilCloFreeVars = cloinfo.ilCloAllFreeVars
+ let ilCloGenericFormals = cloinfo.cloILGenericParams
+ let ilCloGenericActuals = cloinfo.cloSpec.GenericArgs
+ let ilCloTypeRef = cloinfo.cloSpec.TypeRef
+ let ilCloTy = mkILValueTy ilCloTypeRef ilCloGenericActuals
+
+ // The closure implements what ever interfaces the template implements.
+ let interfaceTys = GetImmediateInterfacesOfType SkipUnrefInterfaces.Yes g cenv.amap m templateStructTy
+
+ let ilInterfaceTys = List.map (GenType cenv.amap m eenvinner.tyenv) interfaceTys
+
+ let super = g.iltyp_ValueType
+
+ let templateTyconRef, templateTypeArgs = destAppTy g templateStructTy
+ let templateTypeInst = mkTyconRefInst templateTyconRef templateTypeArgs
+ let eenvinner = AddTemplateReplacement eenvinner (templateTyconRef, ilCloTy, templateTypeInst)
+
+ let infoReader = InfoReader.InfoReader(g, cenv.amap)
+
+ // We codegen the IResumableStateMachine implementation for each generated struct type
+ let getResumptionPointThisVar, getResumptionPointBody =
+ let fieldName = "ResumptionPoint"
+ let thisVar = moveNextThisVar // reusing the this var from the MoveNext implementation
+ let finfo =
+ match infoReader.GetRecordOrClassFieldsOfType(Some fieldName, AccessibilityLogic.AccessorDomain.AccessibleFromSomewhere, m, templateStructTy) with
+ | [finfo] -> finfo
+ | _ -> error(InternalError(sprintf "expected class field %s not found" fieldName, m))
+ thisVar, mkRecdFieldGetViaExprAddr (exprForVal m thisVar, finfo.RecdFieldRef, finfo.TypeInst, m)
+
+ let (getDataThisVar, getDataBody), (setDataThisVar, setDataValueVar, setDataBody) =
+ let fieldName = "Data"
+ let thisVar = moveNextThisVar // reusing the this var from the MoveNext implementation
+ let setDataValueVar, setDataValueExpr = mkCompGenLocal m "value" dataTy
+ let finfo =
+ match infoReader.GetRecordOrClassFieldsOfType(Some fieldName, AccessibilityLogic.AccessorDomain.AccessibleFromSomewhere, m, templateStructTy) with
+ | [finfo] -> finfo
+ | _ -> error(InternalError(sprintf "expected class field %s not found" fieldName, m))
+ (thisVar, mkRecdFieldGetViaExprAddr (exprForVal m thisVar, finfo.RecdFieldRef, finfo.TypeInst, m)),
+ (thisVar, setDataValueVar, mkRecdFieldSetViaExprAddr (exprForVal m thisVar, finfo.RecdFieldRef, finfo.TypeInst, setDataValueExpr, m))
+
+ let methods =
+ [ ((mkLocalValRef moveNextThisVar::thisVars), [], g.mk_IAsyncStateMachine_ty, "MoveNext", moveNextBody);
+ ([mkLocalValRef setStateMachineThisVar], [setStateMachineStateVar], g.mk_IAsyncStateMachine_ty, "SetStateMachine", setStateMachineBody);
+ ([mkLocalValRef getResumptionPointThisVar], [], g.mk_IResumableStateMachine_ty dataTy, "get_ResumptionPoint", getResumptionPointBody);
+ ([mkLocalValRef getDataThisVar], [], g.mk_IResumableStateMachine_ty dataTy, "get_Data", getDataBody);
+ ([mkLocalValRef setDataThisVar], [setDataValueVar], g.mk_IResumableStateMachine_ty dataTy, "set_Data", setDataBody); ]
+
+ let mdefs =
+ [ for (thisVals, argVals, interfaceTy, imethName, bodyR) in methods do
+ let eenvinner = eenvinner |> AddStorageForLocalVals g [(moveNextThisVar, Arg 0) ]
+ let m = bodyR.Range
+ let implementedMeth =
+ match InfoReader.TryFindIntrinsicMethInfo infoReader m AccessibilityLogic.AccessorDomain.AccessibleFromSomewhere imethName interfaceTy with
+ | [meth] when meth.IsInstance -> meth
+ | _ -> error(InternalError(sprintf "expected method %s not found" imethName, m))
+ let argTys = implementedMeth.GetParamTypes(cenv.amap, m, []) |> List.concat
+ let retTy = implementedMeth.GetCompiledReturnTy(cenv.amap, m, [])
+ let ilRetTy = GenReturnType cenv.amap m eenvinner.tyenv retTy
+ let ilArgTys = argTys |> GenTypes cenv.amap m eenvinner.tyenv
+ if ilArgTys.Length <> argVals.Length then
+ error(InternalError(sprintf "expected method arg count of %d, got %d for method %s" argVals.Length ilArgTys.Length imethName, m))
+ let eenvinner = eenvinner |> AddStorageForLocalVals g (thisVals |> List.map (fun v -> (v.Deref, Arg 0)))
+ let eenvinner = eenvinner |> AddStorageForLocalVals g (argVals |> List.mapi (fun i v -> v, Arg (i+1)))
+ let sequel = if retTy.IsNone then discardAndReturnVoid else Return
+ let ilCode = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPSuppress, [], imethName, eenvinner, 1+argVals.Length, bodyR, sequel)
+ let ilParams = (ilArgTys,argVals) ||> List.map2 (fun ty v -> mkILParamNamed(v.LogicalName, ty))
+ mkILNonGenericVirtualMethod(imethName, ILMemberAccess.Public, ilParams, mkILReturn ilRetTy, MethodBody.IL (notlazy ilCode)) ]
+
+ let mimpls =
+ [ for ((_thisVals, _argVals, interfaceTy, imethName, bodyR), mdef) in (List.zip methods mdefs) do
+ let m = bodyR.Range
+ let implementedMeth =
+ match InfoReader.TryFindIntrinsicMethInfo infoReader m AccessibilityLogic.AccessorDomain.AccessibleFromSomewhere imethName interfaceTy with
+ | [meth] when meth.IsInstance -> meth
+ | _ -> error(InternalError(sprintf "expected method %s not found" imethName, m))
+
+ let slotsig = implementedMeth.GetSlotSig(amap, m)
+ let ilOverridesSpec = GenOverridesSpec cenv eenvinner slotsig m
+ let ilOverrideBy = mkILInstanceMethSpecInTy(ilCloTy, imethName, mdef.ParameterTypes, mdef.Return.Type, [])
+ { Overrides = ilOverridesSpec
+ OverrideBy = ilOverrideBy } ]
+
+ let fdefs =
+ [ // Fields copied from the template struct
+ for templateFld in infoReader.GetRecordOrClassFieldsOfType (None, AccessibilityLogic.AccessorDomain.AccessibleFromSomewhere, m, templateStructTy) do
+ // Suppress the "ResumptionDynamicInfo" from generated state machines
+ if templateFld.Name <> "ResumptionDynamicInfo" then
+ let access = ComputeMemberAccess false
+ let fty = GenType cenv.amap m eenvinner.tyenv templateFld.FieldType
+ let fdef =
+ ILFieldDef(name = templateFld.Name, fieldType = fty, attributes = enum 0, data = None, literalValue = None, offset = None, marshal = None, customAttrs = mkILCustomAttrs [])
+ .WithAccess(access)
+ .WithStatic(false)
+ yield fdef
+
+ // Fields for captured variables
+ for ilCloFreeVar in ilCloFreeVars do
+ let access = ComputeMemberAccess false
+ let fdef =
+ ILFieldDef(name = ilCloFreeVar.fvName, fieldType = ilCloFreeVar.fvType, attributes = enum 0,
+ data = None, literalValue = None, offset = None, marshal = None, customAttrs = mkILCustomAttrs [])
+ .WithAccess(access)
+ .WithStatic(false)
+ yield fdef ]
+
+ let cloTypeDef =
+ ILTypeDef(name = ilCloTypeRef.Name,
+ layout = ILTypeDefLayout.Auto,
+ attributes = enum 0,
+ genericParams = ilCloGenericFormals,
+ customAttrs = mkILCustomAttrs([ g.CompilerGeneratedAttribute; mkCompilationMappingAttr g (int SourceConstructFlags.Closure) ]),
+ fields = mkILFields fdefs,
+ events= emptyILEvents,
+ properties = emptyILProperties,
+ methods= mkILMethods mdefs,
+ methodImpls = mkILMethodImpls mimpls,
+ nestedTypes = emptyILTypeDefs,
+ implements = ilInterfaceTys,
+ extends = Some super,
+ securityDecls = emptyILSecurityDecls)
+ .WithSealed(true)
+ .WithSpecialName(true)
+ .WithAccess(ComputeTypeAccess ilCloTypeRef true)
+ .WithLayout(ILTypeDefLayout.Auto)
+ .WithEncoding(ILDefaultPInvokeEncoding.Auto)
+ .WithInitSemantics(ILTypeInit.BeforeField)
+
+ cgbuf.mgbuf.AddTypeDef(ilCloTypeRef, cloTypeDef, false, false, None)
+
+ CountClosure()
+ LocalScope "machine" cgbuf (fun scopeMarks ->
+ let ilMachineAddrTy = ILType.Byref ilCloTy
+
+ // The local for the state machine
+ let locIdx, realloc, _ = AllocLocal cenv cgbuf eenvinner true (g.CompilerGlobalState.Value.IlxGenNiceNameGenerator.FreshCompilerGeneratedName ("machine", m), ilCloTy, false) scopeMarks
+
+ // The local for the state machine address
+ let locIdx2, _realloc2, _ = AllocLocal cenv cgbuf eenvinner true (g.CompilerGlobalState.Value.IlxGenNiceNameGenerator.FreshCompilerGeneratedName (afterCodeThisVar.DisplayName, m), ilMachineAddrTy, false) scopeMarks
+
+ // Zero-initialize the machine
+ EmitInitLocal cgbuf ilCloTy locIdx
+
+ // Initialize the address-of-machine local
+ CG.EmitInstr cgbuf (pop 0) (Push [ ilMachineAddrTy ]) (I_ldloca (uint16 locIdx) )
+ CG.EmitInstr cgbuf (pop 1) (Push [ ]) (I_stloc (uint16 locIdx2) )
+
+ let eenvinner = eenvinner |> AddStorageForLocalVals g [(afterCodeThisVar, Local (locIdx2, realloc, None)) ]
+
+ // Initialize the closure variables
+ for (fv, ilv) in Seq.zip cloFreeVars cloinfo.ilCloAllFreeVars do
+ if stateVarsSet.Contains fv then
+ // zero-initialize the state var
+ if realloc then
+ CG.EmitInstr cgbuf (pop 0) (Push [ ilMachineAddrTy ]) (I_ldloc (uint16 locIdx2) )
+ GenDefaultValue cenv cgbuf eenvouter (fv.Type, m)
+ CG.EmitInstr cgbuf (pop 2) (Push [ ]) (mkNormalStfld (mkILFieldSpecInTy (ilCloTy, ilv.fvName, ilv.fvType)))
+ else
+ // initialize the captured var
+ CG.EmitInstr cgbuf (pop 0) (Push [ ilMachineAddrTy ]) (I_ldloc (uint16 locIdx2) )
+ GenGetLocalVal cenv cgbuf eenvouter m fv None
+ CG.EmitInstr cgbuf (pop 2) (Push [ ]) (mkNormalStfld (mkILFieldSpecInTy (ilCloTy, ilv.fvName, ilv.fvType)))
+
+ // Generate the start expression - eenvinner is used as it contains the binding for machineAddrVar
+ GenExpr cenv cgbuf eenvinner SPSuppress afterCodeBody sequel
+
+ )
+
+and GenObjectExpr cenv cgbuf eenvouter objExpr (baseType, baseValOpt, basecall, overrides, interfaceImpls, m) sequel =
+ let g = cenv.g
+
+ // Find the free variables of the closure, to make them further fields of the object.
+ //
+ // Note, the 'let' bindings for the stateVars have already been transformed to 'set' expressions, and thus the stateVars are now
+ // free variables of the expression.
+ let cloinfo, _, eenvinner = GetIlxClosureInfo cenv m ILBoxity.AsObject false false [] eenvouter objExpr
- let cloAttribs = cloinfo.cloAttribs
let ilCloLambdas = cloinfo.ilCloLambdas
let cloName = cloinfo.cloName
+ let cloSpec = cloinfo.cloSpec
let ilCloAllFreeVars = cloinfo.ilCloAllFreeVars
let ilCloGenericFormals = cloinfo.cloILGenericParams
let ilCloGenericActuals = cloinfo.cloSpec.GenericArgs
let ilCloRetTy = cloinfo.ilCloFormalReturnTy
- let ilCloTypeRef = cloinfo.cloSpec.TypeRef
+ let ilCloTypeRef = cloSpec.TypeRef
let ilTyForOverriding = mkILBoxedTy ilCloTypeRef ilCloGenericActuals
let eenvinner = bindBaseOrThisVarOpt cenv eenvinner baseValOpt
@@ -4579,14 +4903,19 @@ and GenObjectExpr cenv cgbuf eenvouter expr (baseType, baseValOpt, basecall, ove
let interfaceTys = interfaceImpls |> List.map (fst >> GenType cenv.amap m eenvinner.tyenv)
- let attrs = GenAttrs cenv eenvinner cloAttribs
let super = (if isInterfaceTy g baseType then g.ilg.typ_Object else ilCloRetTy)
let interfaceTys = interfaceTys @ (if isInterfaceTy g baseType then [ilCloRetTy] else [])
- let cloTypeDefs = GenClosureTypeDefs cenv (ilCloTypeRef, ilCloGenericFormals, attrs, ilCloAllFreeVars, ilCloLambdas, ilCtorBody, mdefs, mimpls, super, interfaceTys, Some cloinfo.cloSpec)
+ let cloTypeDefs = GenClosureTypeDefs cenv (ilCloTypeRef, ilCloGenericFormals, [], ilCloAllFreeVars, ilCloLambdas, ilCtorBody, mdefs, mimpls, super, interfaceTys, Some cloinfo.cloSpec)
for cloTypeDef in cloTypeDefs do
cgbuf.mgbuf.AddTypeDef(ilCloTypeRef, cloTypeDef, false, false, None)
- GenClosureAlloc cenv cgbuf eenvouter (cloinfo, m)
+
+ CountClosure()
+ GenWitnessArgsFromWitnessInfos cenv cgbuf eenvouter m cloinfo.cloWitnessInfos
+ for fv in cloinfo.cloFreeVars do
+ GenGetLocalVal cenv cgbuf eenvouter m fv None
+
+ CG.EmitInstr cgbuf (pop ilCloAllFreeVars.Length) (Push [ EraseClosures.mkTyOfLambdas g.ilxPubCloEnv ilCloLambdas]) (I_newobj (cloSpec.Constructor, None))
GenSequel cenv eenvouter.cloc cgbuf sequel
and GenSequenceExpr
@@ -4604,8 +4933,8 @@ and GenSequenceExpr
eenvouter |> AddStorageForLocalVals g (stateVars |> List.map (fun v -> v.Deref, Local(0, false, None)))
// Get the free variables. Make a lambda to pretend that the 'nextEnumeratorValRef' is bound (it is an argument to GenerateNext)
- let (cloAttribs, cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilCloTypeRef: ILTypeRef, ilCloAllFreeVars, eenvinner) =
- GetIlxClosureFreeVars cenv m [] eenvouter [] (mkLambda m nextEnumeratorValRef.Deref (generateNextExpr, g.int32_ty))
+ let (cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilCloTypeRef: ILTypeRef, ilCloAllFreeVars, eenvinner) =
+ GetIlxClosureFreeVars cenv m [] ILBoxity.AsObject eenvouter [] (mkLambda m nextEnumeratorValRef.Deref (generateNextExpr, g.int32_ty))
let ilCloSeqElemTy = GenType cenv.amap m eenvinner.tyenv seqElemTy
let cloRetTy = mkSeqTy g seqElemTy
@@ -4613,7 +4942,7 @@ and GenSequenceExpr
let ilCloRetTyOuter = GenType cenv.amap m eenvouter.tyenv cloRetTy
let ilCloEnumeratorTy = GenType cenv.amap m eenvinner.tyenv (mkIEnumeratorTy g seqElemTy)
let ilCloEnumerableTy = GenType cenv.amap m eenvinner.tyenv (mkSeqTy g seqElemTy)
- let ilCloBaseTy = GenType cenv.amap m eenvinner.tyenv (mkAppTy g.seq_base_tcr [seqElemTy])
+ let ilCloBaseTy = GenType cenv.amap m eenvinner.tyenv (g.mk_GeneratedSequenceBase_ty seqElemTy)
let ilCloGenericParams = GenGenericParams cenv eenvinner cloFreeTyvars
// Create a new closure class with a single "MoveNext" method that implements the iterator.
@@ -4671,9 +5000,8 @@ and GenSequenceExpr
let ilCtorBody =
mkILSimpleStorageCtor(None, Some ilCloBaseTy.TypeSpec, ilCloTyInner, [], [], ILMemberAccess.Assembly).MethodBody
- let attrs = GenAttrs cenv eenvinner cloAttribs
let cloMethods = [generateNextMethod; closeMethod; checkCloseMethod; lastGeneratedMethod; getFreshMethod]
- let cloTypeDefs = GenClosureTypeDefs cenv (ilCloTypeRef, ilCloGenericParams, attrs, ilCloAllFreeVars, ilCloLambdas, ilCtorBody, cloMethods, [], ilCloBaseTy, [], Some ilxCloSpec)
+ let cloTypeDefs = GenClosureTypeDefs cenv (ilCloTypeRef, ilCloGenericParams, [], ilCloAllFreeVars, ilCloLambdas, ilCtorBody, cloMethods, [], ilCloBaseTy, [], Some ilxCloSpec)
for cloTypeDef in cloTypeDefs do
cgbuf.mgbuf.AddTypeDef(ilCloTypeRef, cloTypeDef, false, false, None)
@@ -4757,7 +5085,7 @@ and GenGenericArgs m (tyenv: TypeReprEnv) tps =
/// Generate a local type function contract class and implementation
and GenClosureAsLocalTypeFunction cenv (cgbuf: CodeGenBuffer) eenv isLocalTypeFunc thisVars expr m =
let g = cenv.g
- let cloinfo, body, eenvinner = GetIlxClosureInfo cenv m isLocalTypeFunc true thisVars eenv expr
+ let cloinfo, body, eenvinner = GetIlxClosureInfo cenv m ILBoxity.AsObject isLocalTypeFunc true thisVars eenv expr
let ilCloTypeRef = cloinfo.cloSpec.TypeRef
let entryPointInfo = thisVars |> List.map (fun v -> (v, BranchCallClosure (cloinfo.cloArityInfo)))
// Now generate the actual closure implementation w.r.t. eenvinner
@@ -4784,7 +5112,7 @@ and GenClosureAsLocalTypeFunction cenv (cgbuf: CodeGenBuffer) eenv isLocalTypeFu
and GenClosureAsFirstClassFunction cenv (cgbuf: CodeGenBuffer) eenv isLocalTypeFunc thisVars m expr =
let g = cenv.g
- let cloinfo, body, eenvinner = GetIlxClosureInfo cenv m isLocalTypeFunc true thisVars eenv expr
+ let cloinfo, body, eenvinner = GetIlxClosureInfo cenv m ILBoxity.AsObject isLocalTypeFunc true thisVars eenv expr
let entryPointInfo = thisVars |> List.map (fun v -> (v, BranchCallClosure (cloinfo.cloArityInfo)))
let ilCloTypeRef = cloinfo.cloSpec.TypeRef
@@ -4847,7 +5175,7 @@ and GenFreevar cenv m eenvouter tyenvinner (fv: Val) =
#endif
| _ -> GenType cenv.amap m tyenvinner fv.Type
-and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) eenvouter takenNames expr =
+and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) boxity eenvouter takenNames expr =
let g = cenv.g
// Choose a base name for the closure
@@ -4902,13 +5230,11 @@ and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) eenvouter takenNames ex
let cloFreeTyvars = cloFreeTyvars.FreeTypars |> Zset.elements
- let cloAttribs = []
-
let eenvinner = eenvouter |> EnvForTypars cloFreeTyvars
let ilCloTyInner =
let ilCloGenericParams = GenGenericParams cenv eenvinner cloFreeTyvars
- mkILFormalBoxedTy ilCloTypeRef ilCloGenericParams
+ mkILFormalNamedTy boxity ilCloTypeRef ilCloGenericParams
// If generating a named closure, add the closure itself as a var, available via "arg0" .
// The latter doesn't apply for the delegate implementation of closures.
@@ -4956,15 +5282,15 @@ and GetIlxClosureFreeVars cenv m (thisVars: ValRef list) eenvouter takenNames ex
let eenvinner = eenvinner |> AddStorageForLocalVals g ilCloFreeVarStorage
// Return a various results
- (cloAttribs, cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilCloTypeRef, ilCloAllFreeVars, eenvinner)
+ (cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilCloTypeRef, ilCloAllFreeVars, eenvinner)
+
-and GetIlxClosureInfo cenv m isLocalTypeFunc canUseStaticField thisVars eenvouter expr =
+and GetIlxClosureInfo cenv m boxity isLocalTypeFunc canUseStaticField thisVars eenvouter expr =
let g = cenv.g
let returnTy =
match expr with
| Expr.Lambda (_, _, _, _, _, _, returnTy) | Expr.TyLambda (_, _, _, _, returnTy) -> returnTy
- | Expr.Obj (_, ty, _, _, _, _, _) -> ty
- | _ -> failwith "GetIlxClosureInfo: not a lambda expression"
+ | _ -> tyOfExpr g expr
// Determine the structure of the closure. We do this before analyzing free variables to
// determine the taken argument names.
@@ -4985,8 +5311,8 @@ and GetIlxClosureInfo cenv m isLocalTypeFunc canUseStaticField thisVars eenvoute
let takenNames = vs |> List.map (fun v -> v.CompiledName g.CompilerGlobalState)
// Get the free variables and the information about the closure, add the free variables to the environment
- let (cloAttribs, cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilCloTypeRef, ilCloAllFreeVars, eenvinner) =
- GetIlxClosureFreeVars cenv m thisVars eenvouter takenNames expr
+ let (cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilCloTypeRef, ilCloAllFreeVars, eenvinner) =
+ GetIlxClosureFreeVars cenv m thisVars boxity eenvouter takenNames expr
// Put the type and value arguments into the environment
let rec getClosureArgs eenv numArgs tvsl (vs: Val list) =
@@ -5035,8 +5361,7 @@ and GetIlxClosureInfo cenv m isLocalTypeFunc canUseStaticField thisVars eenvoute
cloILGenericParams = ilCloGenericFormals
cloFreeVars=cloFreeVars
cloFreeTyvars=cloFreeTyvars
- cloWitnessInfos = cloWitnessInfos
- cloAttribs=cloAttribs }
+ cloWitnessInfos = cloWitnessInfos }
cloinfo, body, eenvinner
/// Generate a new delegate construction including a closure class if necessary. This is a lot like generating function closures
@@ -5067,8 +5392,8 @@ and GenDelegateExpr cenv cgbuf eenvouter expr (TObjExprMethod((TSlotSig(_, deleg
// Work out the free type variables for the morphing thunk
let takenNames = List.map nameOfVal tmvs
- let (cloAttribs, cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilDelegeeTypeRef, ilCloAllFreeVars, eenvinner) =
- GetIlxClosureFreeVars cenv m [] eenvouter takenNames expr
+ let (cloFreeTyvars, cloWitnessInfos, cloFreeVars, ilDelegeeTypeRef, ilCloAllFreeVars, eenvinner) =
+ GetIlxClosureFreeVars cenv m [] ILBoxity.AsObject eenvouter takenNames expr
let ilDelegeeGenericParams = GenGenericParams cenv eenvinner cloFreeTyvars
let ilDelegeeGenericActualsInner = mkILFormalGenericArgs 0 ilDelegeeGenericParams
@@ -5102,10 +5427,9 @@ and GenDelegateExpr cenv cgbuf eenvouter expr (TObjExprMethod((TSlotSig(_, deleg
let ilCtorBody = delegeeCtorMeth.MethodBody
let ilCloLambdas = Lambdas_return ilCtxtDelTy
- let ilAttribs = GenAttrs cenv eenvinner cloAttribs
let cloTypeDefs =
(if useStaticClosure then GenStaticDelegateClosureTypeDefs else GenClosureTypeDefs)
- cenv (ilDelegeeTypeRef, ilDelegeeGenericParams, ilAttribs, ilCloAllFreeVars, ilCloLambdas, ilCtorBody, [delegeeInvokeMeth], [], g.ilg.typ_Object, [], None)
+ cenv (ilDelegeeTypeRef, ilDelegeeGenericParams, [], ilCloAllFreeVars, ilCloLambdas, ilCtorBody, [delegeeInvokeMeth], [], g.ilg.typ_Object, [], None)
for cloTypeDef in cloTypeDefs do
cgbuf.mgbuf.AddTypeDef(ilDelegeeTypeRef, cloTypeDef, false, false, None)
CountClosure()
@@ -5239,7 +5563,7 @@ and GenDecisionTreeAndTargetsInner cenv cgbuf inplabOpt stackAtTargets eenv tree
let startScope, endScope as scopeMarks = StartDelayedLocalScope "dtreeBind" cgbuf
let eenv = AllocStorageForBind cenv cgbuf scopeMarks eenv bind
let sp = GenDebugPointForBind cenv cgbuf bind
- GenBindingAfterDebugPoint cenv cgbuf eenv sp bind (Some startScope)
+ GenBindingAfterDebugPoint cenv cgbuf eenv sp bind false (Some startScope)
// We don't get the scope marks quite right for dtree-bound variables. This is because
// we effectively lose an EndLocalScope for all dtrees that go to the same target
// So we just pretend that the variable goes out of scope here.
@@ -5265,11 +5589,11 @@ and GetTarget (targets:_[]) n =
/// If inplabOpt is present, this label must get set to the first logical place to execute.
/// For example, if no variables get bound this can just be set to jump straight to the target.
and GenDecisionTreeSuccess cenv cgbuf inplabOpt stackAtTargets eenv es targetIdx targets (targetNext: int ref, targetCounts: Dictionary) repeatSP targetInfos sequel =
- let (TTarget(vs, successExpr, spTarget)) = GetTarget targets targetIdx
+ let (TTarget(vs, successExpr, spTarget, flags)) = GetTarget targets targetIdx
match IntMap.tryFind targetIdx targetInfos with
| Some (targetInfo, isTargetPostponed) ->
- let (targetMarkBeforeBinds, targetMarkAfterBinds: Mark, eenvAtTarget, _, _, _, _, _, _, _) = targetInfo
+ let (targetMarkBeforeBinds, targetMarkAfterBinds: Mark, eenvAtTarget, _, _, _, _, _, _, _, _) = targetInfo
// We have encountered this target before. See if we should generate it now
let targetCount = targetCounts.[targetIdx]
@@ -5287,6 +5611,7 @@ and GenDecisionTreeSuccess cenv cgbuf inplabOpt stackAtTargets eenv es targetIdx
(vs, es) ||> List.iter2 (fun v e ->
+ GetStoreValCtxt cenv cgbuf eenvAtTarget v
// Emit the expression
GenBindingRhs cenv cgbuf eenv SPSuppress v e)
@@ -5315,9 +5640,16 @@ and GenDecisionTreeSuccess cenv cgbuf inplabOpt stackAtTargets eenv es targetIdx
let targetMarkBeforeBinds = CG.GenerateDelayMark cgbuf "targetBeforeBinds"
let targetMarkAfterBinds = CG.GenerateDelayMark cgbuf "targetAfterBinds"
let startScope, endScope as scopeMarks = StartDelayedLocalScope "targetBinds" cgbuf
- let binds = mkInvisibleBinds vs es
+ // Allocate storage for variables (except those lifted to be state machine variables)
+ let binds =
+ match flags with
+ | None -> mkInvisibleBinds vs es
+ | Some stateVarFlags ->
+ (vs, es, stateVarFlags)
+ |||> List.zip3
+ |> List.choose (fun (v, e, flag) -> if flag then None else Some (mkInvisibleBind v e))
let eenvAtTarget = AllocStorageForBinds cenv cgbuf scopeMarks eenv binds
- let targetInfo = (targetMarkBeforeBinds, targetMarkAfterBinds, eenvAtTarget, successExpr, spTarget, repeatSP, vs, binds, startScope, endScope)
+ let targetInfo = (targetMarkBeforeBinds, targetMarkAfterBinds, eenvAtTarget, successExpr, spTarget, repeatSP, vs, es, flags, startScope, endScope)
let targetCount = targetCounts.[targetIdx]
@@ -5342,7 +5674,8 @@ and GenDecisionTreeSuccess cenv cgbuf inplabOpt stackAtTargets eenv es targetIdx
let targetInfos = IntMap.add targetIdx (targetInfo, isTargetPostponed) targetInfos
targetInfos, genTargetInfoOpt
-and GenDecisionTreeTarget cenv cgbuf stackAtTargets (targetMarkBeforeBinds, targetMarkAfterBinds, eenvAtTarget, successExpr, spTarget, repeatSP, vs, binds, startScope, endScope) sequel =
+and GenDecisionTreeTarget cenv cgbuf stackAtTargets targetInfo sequel =
+ let (targetMarkBeforeBinds, targetMarkAfterBinds, eenvAtTarget, successExpr, spTarget, repeatSP, vs, es, flags, startScope, endScope) = targetInfo
CG.SetMarkToHere cgbuf targetMarkBeforeBinds
let spExpr = (match spTarget with DebugPointForTarget.Yes -> SPAlways | DebugPointForTarget.No _ -> SPSuppress)
@@ -5360,7 +5693,8 @@ and GenDecisionTreeTarget cenv cgbuf stackAtTargets (targetMarkBeforeBinds, targ
| DebugPointForTarget.No -> cgbuf.EmitStartOfHiddenCode()
CG.SetMarkToHere cgbuf startScope
- GenBindings cenv cgbuf eenvAtTarget binds
+ let binds = mkInvisibleBinds vs es
+ GenBindings cenv cgbuf eenvAtTarget binds flags
CG.SetMarkToHere cgbuf targetMarkAfterBinds
CG.SetStack cgbuf stackAtTargets
(eenvAtTarget, spExpr, successExpr, (EndLocalScope(sequel, endScope)))
@@ -5375,7 +5709,7 @@ and GenDecisionTreeSwitch cenv cgbuf inplabOpt stackAtTargets eenv e cases defau
// optimize a test against a boolean value, i.e. the all-important if-then-else
| TCase(DecisionTreeTest.Const(Const.Bool b), successTree) :: _ ->
let failureTree = (match defaultTargetOpt with None -> cases.Tail.Head.CaseTree | Some d -> d)
- GenDecisionTreeTest cenv eenv.cloc cgbuf stackAtTargets e None eenv (if b then successTree else failureTree) (if b then failureTree else successTree) targets targetCounts repeatSP targetInfos sequel contf
+ GenDecisionTreeTest cenv eenv.cloc cgbuf stackAtTargets e None false eenv (if b then successTree else failureTree) (if b then failureTree else successTree) targets targetCounts repeatSP targetInfos sequel contf
// // Remove a single test for a union case . Union case tests are always exa
//| [ TCase(DecisionTreeTest.UnionCase _, successTree) ] when (defaultTargetOpt.IsNone) ->
@@ -5393,7 +5727,18 @@ and GenDecisionTreeSwitch cenv cgbuf inplabOpt stackAtTargets eenv e cases defau
let idx = c.Index
let avoidHelpers = entityRefInThisAssembly g.compilingFslib c.TyconRef
let tester = (Some (pop 1, Push [g.ilg.typ_Bool], Choice1Of2 (avoidHelpers, cuspec, idx)))
- GenDecisionTreeTest cenv eenv.cloc cgbuf stackAtTargets e tester eenv successTree failureTree targets targetCounts repeatSP targetInfos sequel contf
+ GenDecisionTreeTest cenv eenv.cloc cgbuf stackAtTargets e tester false eenv successTree failureTree targets targetCounts repeatSP targetInfos sequel contf
+
+ // Use GenDecisionTreeTest to generate a single test for null (when no box required) where the success
+ // is going to the immediate first node in the tree
+ | TCase(DecisionTreeTest.IsNull _, (TDSuccess([], 0) as successTree)) :: rest
+ when rest.Length = (match defaultTargetOpt with None -> 1 | Some _ -> 0)
+ && not (isTyparTy g (tyOfExpr g e)) ->
+ let failureTree =
+ match defaultTargetOpt with
+ | None -> rest.Head.CaseTree
+ | Some tg -> tg
+ GenDecisionTreeTest cenv eenv.cloc cgbuf stackAtTargets e None true eenv successTree failureTree targets targetCounts repeatSP targetInfos sequel contf
| _ ->
let caseLabels = List.map (fun _ -> CG.GenerateDelayMark cgbuf "switch_case") cases
@@ -5411,7 +5756,7 @@ and GenDecisionTreeSwitch cenv cgbuf inplabOpt stackAtTargets eenv e cases defau
| DecisionTreeTest.Const(Const.Zero) ->
GenExpr cenv cgbuf eenv SPSuppress e Continue
BI_brfalse
- | DecisionTreeTest.IsNull ->
+ | DecisionTreeTest.IsNull ->
GenExpr cenv cgbuf eenv SPSuppress e Continue
let srcTy = tyOfExpr g e
if isTyparTy g srcTy then
@@ -5506,7 +5851,7 @@ and GenDecisionTreeCases cenv cgbuf stackAtTargets eenv defaultTargetOpt targets
// Used for the peephole optimization below
and (|BoolExpr|_|) = function Expr.Const (Const.Bool b1, _, _) -> Some b1 | _ -> None
-and GenDecisionTreeTest cenv cloc cgbuf stackAtTargets e tester eenv successTree failureTree targets targetCounts repeatSP targetInfos sequel contf =
+and GenDecisionTreeTest cenv cloc cgbuf stackAtTargets e tester isNullTest eenv successTree failureTree targets targetCounts repeatSP targetInfos sequel contf =
let g = cenv.g
match successTree, failureTree with
@@ -5514,14 +5859,15 @@ and GenDecisionTreeTest cenv cloc cgbuf stackAtTargets e tester eenv successTree
// This comes up in the generated equality functions. REVIEW: do this as a peephole optimization elsewhere
| TDSuccess(es1, n1),
TDSuccess(es2, n2) when
+ not isNullTest &&
isNil es1 && isNil es2 &&
(match GetTarget targets n1, GetTarget targets n2 with
- | TTarget(_, BoolExpr b1, _), TTarget(_, BoolExpr b2, _) -> b1 = not b2
+ | TTarget(_, BoolExpr b1, _, _), TTarget(_, BoolExpr b2, _, _) -> b1 = not b2
| _ -> false) ->
match GetTarget targets n1, GetTarget targets n2 with
- | TTarget(_, BoolExpr b1, _), _ ->
+ | TTarget(_, BoolExpr b1, _, _), _ ->
GenExpr cenv cgbuf eenv SPSuppress e Continue
match tester with
| Some (pops, pushes, i) ->
@@ -5539,7 +5885,8 @@ and GenDecisionTreeTest cenv cloc cgbuf stackAtTargets e tester eenv successTree
| _ ->
match tester with
- | None _ ->
+ | None ->
+
// Check if there is more logic in the decision tree for the failure branch
// (and no more logic for the success branch), for example
// when emitting the first part of 'expr1 || expr2'.
@@ -5551,7 +5898,8 @@ and GenDecisionTreeTest cenv cloc cgbuf stackAtTargets e tester eenv successTree
// OK, there is more logic in the decision tree on the failure branch
let success = CG.GenerateDelayMark cgbuf "testSuccess"
- GenExpr cenv cgbuf eenv SPSuppress e (CmpThenBrOrContinue(pop 1, [ I_brcmp (BI_brtrue, success.CodeLabel) ]))
+ let testForSuccess = if isNullTest then BI_brfalse else BI_brtrue
+ GenExpr cenv cgbuf eenv SPSuppress e (CmpThenBrOrContinue(pop 1, [ I_brcmp (testForSuccess, success.CodeLabel) ]))
GenDecisionTreeAndTargetsInner cenv cgbuf None stackAtTargets eenv failureTree targets targetCounts repeatSP targetInfos sequel (fun targetInfos ->
GenDecisionTreeAndTargetsInner cenv cgbuf (Some success) stackAtTargets eenv successTree targets targetCounts repeatSP targetInfos sequel contf
)
@@ -5562,7 +5910,8 @@ and GenDecisionTreeTest cenv cloc cgbuf stackAtTargets e tester eenv successTree
// in the decision tree on the failure branch. Continue doing the success branch
// logic first.
let failure = CG.GenerateDelayMark cgbuf "testFailure"
- GenExpr cenv cgbuf eenv SPSuppress e (CmpThenBrOrContinue(pop 1, [ I_brcmp (BI_brfalse, failure.CodeLabel) ]))
+ let testForFailure = if isNullTest then BI_brtrue else BI_brfalse
+ GenExpr cenv cgbuf eenv SPSuppress e (CmpThenBrOrContinue(pop 1, [ I_brcmp (testForFailure, failure.CodeLabel) ]))
GenDecisionTreeAndTargetsInner cenv cgbuf None stackAtTargets eenv successTree targets targetCounts repeatSP targetInfos sequel (fun targetInfos ->
GenDecisionTreeAndTargetsInner cenv cgbuf (Some failure) stackAtTargets eenv failureTree targets targetCounts repeatSP targetInfos sequel contf
)
@@ -5615,7 +5964,7 @@ and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) =
let isLocalTypeFunc = Option.isSome thisVars && (IsNamedLocalTypeFuncVal cenv.g (Option.get thisVars) e)
let thisVars = (match e with Expr.Obj _ -> [] | _ when isLocalTypeFunc -> [] | _ -> Option.map mkLocalValRef thisVars |> Option.toList)
let canUseStaticField = (match e with Expr.Obj _ -> false | _ -> true)
- let clo, _, eenvclo = GetIlxClosureInfo cenv m isLocalTypeFunc canUseStaticField thisVars {eenv with letBoundVars=(mkLocalValRef boundv) :: eenv.letBoundVars} e
+ let clo, _, eenvclo = GetIlxClosureInfo cenv m ILBoxity.AsObject isLocalTypeFunc canUseStaticField thisVars {eenv with letBoundVars=(mkLocalValRef boundv) :: eenv.letBoundVars} e
clo.cloFreeVars |> List.iter (fun fv ->
if Zset.contains fv forwardReferenceSet then
match StorageForVal cenv.g m fv eenvclo with
@@ -5645,7 +5994,7 @@ and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) =
// Generate the actual bindings
let _ =
(recursiveVars, allBinds) ||> List.fold (fun forwardReferenceSet (bind: Binding) ->
- GenBinding cenv cgbuf eenv bind
+ GenBinding cenv cgbuf eenv bind false
// Record the variable as defined
let forwardReferenceSet = Zset.remove bind.Var forwardReferenceSet
// Execute and discard any fixups that can now be committed
@@ -5668,9 +6017,9 @@ and GenDebugPointForBind cenv cgbuf bind =
pt |> Option.iter (CG.EmitSeqPoint cgbuf)
sp
-and GenBinding cenv cgbuf eenv bind =
+and GenBinding cenv cgbuf eenv (bind: Binding) (isStateVar: bool) =
let sp = GenDebugPointForBind cenv cgbuf bind
- GenBindingAfterDebugPoint cenv cgbuf eenv sp bind None
+ GenBindingAfterDebugPoint cenv cgbuf eenv sp bind isStateVar None
and ComputeMemberAccessRestrictedBySig eenv vspec =
let isHidden =
@@ -5686,17 +6035,19 @@ and ComputeMethodAccessRestrictedBySig eenv vspec =
vspec.IsIncrClassGeneratedMember // compiler generated members for class function 'let' bindings get assembly visibility
ComputeMemberAccess isHidden
-and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) startScopeMarkOpt =
+and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) isStateVar startScopeMarkOpt =
let g = cenv.g
// Record the closed reflection definition if publishing
// There is no real reason we're doing this so late in the day
match vspec.PublicPath, vspec.ReflectedDefinition with
- | Some _, Some e -> cgbuf.mgbuf.AddReflectedDefinition(vspec, e)
+ | Some _, Some e when not isStateVar -> cgbuf.mgbuf.AddReflectedDefinition(vspec, e)
| _ -> ()
- let eenv = { eenv with letBoundVars = (mkLocalValRef vspec) :: eenv.letBoundVars;
- initLocals = eenv.initLocals && (match vspec.ApparentEnclosingEntity with Parent ref -> not (HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute ref.Attribs) | _ -> true) }
+ let eenv =
+ if isStateVar then eenv
+ else { eenv with letBoundVars = (mkLocalValRef vspec) :: eenv.letBoundVars;
+ initLocals = eenv.initLocals && (match vspec.ApparentEnclosingEntity with Parent ref -> not (HasFSharpAttribute g g.attrib_SkipLocalsInitAttribute ref.Attribs) | _ -> true) }
let access = ComputeMethodAccessRestrictedBySig eenv vspec
@@ -5717,13 +6068,13 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) star
CommitStartScope cgbuf startScopeMarkOpt
// The initialization code for static 'let' and 'do' bindings gets compiled into the initialization .cctor for the whole file
- | _ when vspec.IsClassConstructor && isNil vspec.TopValDeclaringEntity.TyparsNoRange ->
+ | _ when vspec.IsClassConstructor && isNil vspec.TopValDeclaringEntity.TyparsNoRange && not isStateVar ->
let tps, _, _, _, cctorBody, _ = IteratedAdjustArityOfLambda g cenv.amap vspec.ValReprInfo.Value rhsExpr
let eenv = EnvForTypars tps eenv
CommitStartScope cgbuf startScopeMarkOpt
GenExpr cenv cgbuf eenv SPSuppress cctorBody discard
-
- | Method (topValInfo, _, mspec, mspecW, _, ctps, mtps, curriedArgInfos, paramInfos, witnessInfos, argTys, retInfo) ->
+
+ | Method (topValInfo, _, mspec, mspecW, _, ctps, mtps, curriedArgInfos, paramInfos, witnessInfos, argTys, retInfo) when not isStateVar ->
let methLambdaTypars, methLambdaCtorThisValOpt, methLambdaBaseValOpt, methLambdaCurriedVars, methLambdaBody, methLambdaBodyTy =
IteratedAdjustArityOfLambda g cenv.amap topValInfo rhsExpr
@@ -5743,7 +6094,7 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) star
let copyOfLambdaBody = copyExpr cenv.g CloneAll methLambdaBody
generator cenv cgbuf.mgbuf eenv (vspec, mspecW, hasWitnessEntry, true, access, ctps, mtps, witnessInfos, curriedArgInfos, paramInfos, argTys, retInfo, topValInfo, methLambdaCtorThisValOpt, methLambdaBaseValOpt, methLambdaTypars, methLambdaVars, copyOfLambdaBody, methLambdaBodyTy)
- | StaticProperty (ilGetterMethSpec, optShadowLocal) ->
+ | StaticProperty (ilGetterMethSpec, optShadowLocal) when not isStateVar ->
let ilAttribs = GenAttrs cenv eenv vspec.Attribs
let ilTy = ilGetterMethSpec.FormalReturnType
@@ -5860,10 +6211,17 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) star
| Local (_, realloc, _), Expr.Const (Const.Zero, _, _) when not realloc && not (eenv.isInLoop && vspec.IsMutable) ->
CommitStartScope cgbuf startScopeMarkOpt
| _ ->
+ GetStoreValCtxt cenv cgbuf eenv vspec
GenBindingRhs cenv cgbuf eenv SPSuppress vspec rhsExpr
CommitStartScope cgbuf startScopeMarkOpt
GenStoreVal cenv cgbuf eenv vspec.Range vspec
+and GetStoreValCtxt cenv cgbuf eenv (vspec: Val) =
+ // Emit the ldarg0 if needed
+ match StorageForVal cenv.g vspec.Range vspec eenv with
+ | Env (ilCloTy, _, _) -> CG.EmitInstr cgbuf (pop 0) (Push [ilCloTy]) mkLdarg0
+ | _ -> ()
+
//-------------------------------------------------------------------------
// Generate method bindings
//-------------------------------------------------------------------------
@@ -6213,9 +6571,6 @@ and ComputeMethodImplAttribs cenv (_v: Val) attrs =
let hasAggressiveInliningImplFlag = (implflags &&& 0x0100) <> 0x0
hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, hasAggressiveInliningImplFlag, attrs
-and DelayGenMethodForBinding cenv mgbuf eenv ilxMethInfoArgs =
- cenv.delayedGenMethods.Enqueue (fun cenv -> GenMethodForBinding cenv mgbuf eenv ilxMethInfoArgs)
-
and GenMethodForBinding
cenv mgbuf eenv
(v: Val, mspec, hasWitnessEntry, generateWitnessArgs, access, ctps, mtps, witnessInfos, curriedArgInfos, paramInfos, argTys, retInfo, topValInfo,
@@ -6546,8 +6901,13 @@ and GenPInvokeMethod (nm, dll, namedArgs) =
ThrowOnUnmappableChar= if (decoder.FindBool "ThrowOnUnmappableChar" false) then PInvokeThrowOnUnmappableChar.Enabled else PInvokeThrowOnUnmappableChar.UseAssembly
CharBestFit=if (decoder.FindBool "BestFitMapping" false) then PInvokeCharBestFit.Enabled else PInvokeCharBestFit.UseAssembly } : PInvokeMethod
MethodBody.PInvoke(lazy pinvoke)
-
-and GenBindings cenv cgbuf eenv binds = List.iter (GenBinding cenv cgbuf eenv) binds
+
+and GenBindings cenv cgbuf eenv binds flags =
+ match flags with
+ | None ->
+ binds |> List.iter (fun bind -> GenBinding cenv cgbuf eenv bind false)
+ | Some flags ->
+ (binds, flags) ||> List.iter2 (fun bind flag -> GenBinding cenv cgbuf eenv bind flag)
//-------------------------------------------------------------------------
// Generate locals and other storage of values
@@ -6555,11 +6915,7 @@ and GenBindings cenv cgbuf eenv binds = List.iter (GenBinding cenv cgbuf eenv) b
and GenSetVal cenv cgbuf eenv (vref, e, m) sequel =
let storage = StorageForValRef cenv.g m vref eenv
- match storage with
- | Env (ilCloTy, _, _) ->
- CG.EmitInstr cgbuf (pop 0) (Push [ilCloTy]) mkLdarg0
- | _ ->
- ()
+ GetStoreValCtxt cenv cgbuf eenv vref.Deref
GenExpr cenv cgbuf eenv SPSuppress e Continue
GenSetStorage vref.Range cgbuf storage
GenUnitThenSequel cenv eenv m eenv.cloc cgbuf sequel
@@ -6717,7 +7073,7 @@ and GenStoreVal cenv cgbuf eenv m (vspec: Val) =
GenSetStorage vspec.Range cgbuf (StorageForVal cenv.g m vspec eenv)
/// Allocate IL locals
-and AllocLocal cenv cgbuf eenv compgen (v, ty, isFixed) (scopeMarks: Mark * Mark) =
+and AllocLocal cenv cgbuf eenv compgen (v, ty, isFixed) (scopeMarks: Mark * Mark) : int * _ * _ =
// The debug range for the local
let ranges = if compgen then [] else [(v, scopeMarks)]
// Get an index for the local
@@ -6743,7 +7099,7 @@ and AllocLocalVal cenv cgbuf v eenv repr scopeMarks =
let eenvinner =
{eenv with
letBoundVars=(mkLocalValRef v) :: eenv.letBoundVars}
- let cloinfo, _, _ = GetIlxClosureInfo cenv v.Range true true [] eenvinner r
+ let cloinfo, _, _ = GetIlxClosureInfo cenv v.Range ILBoxity.AsObject true true [] eenvinner r
cloinfo
let idx, realloc, eenv = AllocLocal cenv cgbuf eenv v.IsCompilerGenerated (v.CompiledName g.CompilerGlobalState, g.ilg.typ_Object, false) scopeMarks
@@ -7019,7 +7375,7 @@ and GenModuleDef cenv (cgbuf: CodeGenBuffer) qname lazyInitInfo eenv x =
mbinds |> List.iter (GenModuleBinding cenv cgbuf qname lazyInitInfo eenv m)
| TMDefLet(bind, _) ->
- GenBindings cenv cgbuf eenv [bind]
+ GenBindings cenv cgbuf eenv [bind] None
| TMDefDo(e, _) ->
GenExpr cenv cgbuf eenv SPAlways e discard
@@ -7392,7 +7748,7 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) =
| TNoRepr -> ()
| TAsmRepr _ | TILObjectRepr _ | TMeasureableRepr _ -> ()
| TFSharpObjectRepr _ | TRecdRepr _ | TUnionRepr _ ->
- let eenvinner = ReplaceTyenv (TypeReprEnv.ForTycon tycon) eenv
+ let eenvinner = EnvForTycon tycon eenv
let thisTy = generalizedTyconRef tcref
let ilThisTy = GenType cenv.amap m eenvinner.tyenv thisTy
@@ -8123,6 +8479,7 @@ let GetEmptyIlxGenEnv (g: TcGlobals) ccu =
let thisCompLoc = CompLocForCcu ccu
{ tyenv=TypeReprEnv.Empty
cloc = thisCompLoc
+ exitSequel = Return
valsInScope=ValMap<_>.Empty
witnessesInScope = EmptyTraitWitnessInfoHashMap g
suppressWitnesses = false
diff --git a/src/fsharp/InnerLambdasToTopLevelFuncs.fs b/src/fsharp/InnerLambdasToTopLevelFuncs.fs
index fb070b66176..6b08832a7e0 100644
--- a/src/fsharp/InnerLambdasToTopLevelFuncs.fs
+++ b/src/fsharp/InnerLambdasToTopLevelFuncs.fs
@@ -137,7 +137,7 @@ let mkLocalNameTypeArity compgen m name ty topValInfo =
let GetValsBoundUnderMustInline xinfo =
let accRejectFrom (v: Val) repr rejectS =
- if v.InlineInfo = ValInline.PseudoVal then
+ if v.InlineInfo = ValInline.Always then
Zset.union (GetValsBoundInExpr repr) rejectS
else rejectS
let rejectS = Zset.empty valOrder
@@ -157,7 +157,9 @@ let IsRefusedTLR g (f: Val) =
// Special values are instance methods etc. on .NET types. For now leave these alone
let specialVal = f.MemberInfo.IsSome
let alreadyChosen = f.ValReprInfo.IsSome
- let refuseTest = alreadyChosen || mutableVal || byrefVal || specialVal || dllImportStubOrOtherNeverInline
+ let isResumableCode = isReturnsResumableCodeTy g f.Type
+ let isInlineIfLambda = f.InlineIfLambda
+ let refuseTest = alreadyChosen || mutableVal || byrefVal || specialVal || dllImportStubOrOtherNeverInline || isResumableCode || isInlineIfLambda
refuseTest
let IsMandatoryTopLevel (f: Val) =
@@ -1131,7 +1133,7 @@ module Pass4_RewriteAssembly =
// ilobj - has implicit lambda exprs and recursive/base references
| Expr.Obj (_, ty, basev, basecall, overrides, iimpls, m) ->
- let basecall, z = TransExpr penv z basecall
+ let basecall, z = TransExpr penv z basecall
let overrides, z = List.mapFold (TransMethod penv) z overrides
let iimpls, z =
(z, iimpls) ||> List.mapFold (fun z (tType, objExprs) ->
@@ -1291,11 +1293,11 @@ module Pass4_RewriteAssembly =
let dflt, z = Option.mapFold (TransDecisionTree penv) z dflt
TDSwitch (e, cases, dflt, m), z
- and TransDecisionTreeTarget penv z (TTarget(vs, e, spTarget)) =
+ and TransDecisionTreeTarget penv z (TTarget(vs, e, spTarget, flags)) =
let z = EnterInner z
let e, z = TransExpr penv z e
let z = ExitInner z
- TTarget(vs, e, spTarget), z
+ TTarget(vs, e, spTarget, flags), z
and TransValBinding penv z bind = TransBindingRhs penv z bind
and TransValBindings penv z binds = List.mapFold (TransValBinding penv) z binds
diff --git a/src/fsharp/LanguageFeatures.fs b/src/fsharp/LanguageFeatures.fs
index 2e7fd7f4af9..c852582a5b7 100644
--- a/src/fsharp/LanguageFeatures.fs
+++ b/src/fsharp/LanguageFeatures.fs
@@ -29,6 +29,7 @@ type LanguageFeature =
| FromEndSlicing
| FixedIndexSlice3d4d
| AndBang
+ | ResumableStateMachines
| NullableOptionalInterop
| DefaultInterfaceMemberConsumption
| WitnessPassing
@@ -82,6 +83,7 @@ type LanguageVersion (specifiedVersionAsString) =
LanguageFeature.OverloadsForCustomOperations, previewVersion
LanguageFeature.ExpandedMeasurables, previewVersion
LanguageFeature.FromEndSlicing, previewVersion
+ LanguageFeature.ResumableStateMachines, previewVersion
LanguageFeature.StructActivePattern, previewVersion
LanguageFeature.PrintfBinaryFormat, previewVersion
LanguageFeature.UseBindingValueDiscard, previewVersion
@@ -153,6 +155,7 @@ type LanguageVersion (specifiedVersionAsString) =
| LanguageFeature.FromEndSlicing -> FSComp.SR.featureFromEndSlicing()
| LanguageFeature.FixedIndexSlice3d4d -> FSComp.SR.featureFixedIndexSlice3d4d()
| LanguageFeature.AndBang -> FSComp.SR.featureAndBang()
+ | LanguageFeature.ResumableStateMachines -> FSComp.SR.featureResumableStateMachines()
| LanguageFeature.NullableOptionalInterop -> FSComp.SR.featureNullableOptionalInterop()
| LanguageFeature.DefaultInterfaceMemberConsumption -> FSComp.SR.featureDefaultInterfaceMemberConsumption()
| LanguageFeature.WitnessPassing -> FSComp.SR.featureWitnessPassing()
diff --git a/src/fsharp/LanguageFeatures.fsi b/src/fsharp/LanguageFeatures.fsi
index e7e0a9c12a9..b7ee7c18f7a 100644
--- a/src/fsharp/LanguageFeatures.fsi
+++ b/src/fsharp/LanguageFeatures.fsi
@@ -17,6 +17,7 @@ type LanguageFeature =
| FromEndSlicing
| FixedIndexSlice3d4d
| AndBang
+ | ResumableStateMachines
| NullableOptionalInterop
| DefaultInterfaceMemberConsumption
| WitnessPassing
diff --git a/src/fsharp/LowerCallsAndSeqs.fs b/src/fsharp/LowerCallsAndSeqs.fs
index 3acc080f9b9..9ef6657dbe5 100644
--- a/src/fsharp/LowerCallsAndSeqs.fs
+++ b/src/fsharp/LowerCallsAndSeqs.fs
@@ -229,55 +229,55 @@ let (|SeqElemTy|_|) g amap m ty =
/// expressions: one for 'MoveNext' and one for 'Dispose'.
let ConvertSequenceExprToObject g amap overallExpr =
/// Implement a decision to represent a 'let' binding as a non-escaping local variable (rather than a state machine variable)
- let RepresentBindingAsLocal (bind: Binding) res2 m =
+ let RepresentBindingAsLocal (bind: Binding) resBody m =
if verbose then
printfn "LowerSeq: found local variable %s" bind.Var.DisplayName
- { res2 with
+ { resBody with
phase2 = (fun ctxt ->
- let generate2, dispose2, checkDispose2 = res2.phase2 ctxt
- let generate = mkLetBind m bind generate2
- let dispose = dispose2
- let checkDispose = checkDispose2
+ let generateBody, disposeBody, checkDisposeBody = resBody.phase2 ctxt
+ let generate = mkLetBind m bind generateBody
+ let dispose = disposeBody
+ let checkDispose = checkDisposeBody
generate, dispose, checkDispose)
- stateVars = res2.stateVars }
+ stateVars = resBody.stateVars }
/// Implement a decision to represent a 'let' binding as a state machine variable
- let RepresentBindingAsStateMachineLocal (bind: Binding) res2 m =
+ let RepresentBindingAsStateMachineLocal (bind: Binding) resBody m =
if verbose then
printfn "LowerSeq: found state variable %s" bind.Var.DisplayName
let (TBind(v, e, sp)) = bind
let sp, spm =
match sp with
- | DebugPointAtBinding.Yes m -> DebugPointAtSequential.Both, m
- | _ -> DebugPointAtSequential.StmtOnly, e.Range
+ | DebugPointAtBinding.Yes m -> DebugPointAtSequential.SuppressNeither, m
+ | _ -> DebugPointAtSequential.SuppressStmt, e.Range
let vref = mkLocalValRef v
- { res2 with
+ { resBody with
phase2 = (fun ctxt ->
- let generate2, dispose2, checkDispose2 = res2.phase2 ctxt
+ let generateBody, disposeBody, checkDisposeBody = resBody.phase2 ctxt
let generate =
mkCompGenSequential m
(mkSequential sp m
(mkValSet spm vref e)
- generate2)
+ generateBody)
// zero out the current value to free up its memory
(mkValSet m vref (mkDefault (m, vref.Type)))
- let dispose = dispose2
- let checkDispose = checkDispose2
+ let dispose = disposeBody
+ let checkDispose = checkDisposeBody
generate, dispose, checkDispose)
- stateVars = vref :: res2.stateVars }
+ stateVars = vref :: resBody.stateVars }
- let RepresentBindingsAsLifted mkBinds res2 =
+ let RepresentBindingsAsLifted mkBinds resBody =
if verbose then
printfn "found top level let "
- { res2 with
+ { resBody with
phase2 = (fun ctxt ->
- let generate2, dispose2, checkDispose2 = res2.phase2 ctxt
- let generate = mkBinds generate2
- let dispose = dispose2
- let checkDispose = checkDispose2
+ let generateBody, disposeBody, checkDisposeBody = resBody.phase2 ctxt
+ let generate = mkBinds generateBody
+ let dispose = disposeBody
+ let checkDispose = checkDisposeBody
generate, dispose, checkDispose) }
let rec ConvertSeqExprCode
@@ -297,7 +297,7 @@ let ConvertSequenceExprToObject g amap overallExpr =
let label = IL.generateCodeLabel()
Some { phase2 = (fun (pcVar, currVar, _nextv, pcMap) ->
let generate =
- mkSequential DebugPointAtSequential.Both m
+ mkSequential DebugPointAtSequential.SuppressNeither m
(mkValSet m pcVar (mkInt32 g m pcMap.[label]))
(mkCompGenSequential m
(mkValSet m currVar e)
@@ -339,7 +339,7 @@ let ConvertSequenceExprToObject g amap overallExpr =
Some { phase2 = (fun ctxt ->
let generate1, dispose1, checkDispose1 = res1.phase2 ctxt
let generate2, dispose2, checkDispose2 = res2.phase2 ctxt
- let generate = mkSequential DebugPointAtSequential.Both m generate1 generate2
+ let generate = mkSequential DebugPointAtSequential.SuppressNeither m generate1 generate2
// Order shouldn't matter here, since disposals actions are linked together by goto's (each ends in a goto).
// However leaving as is for now.
let dispose = mkCompGenSequential m dispose2 dispose1
@@ -416,7 +416,7 @@ let ConvertSequenceExprToObject g amap overallExpr =
let compensation = copyExpr g CloneAllAndMarkExprValsAsCompilerGenerated compensation
mkCompGenSequential m
// set the PC to the inner finally, so that if an exception happens we run the right finally
- (mkSequential DebugPointAtSequential.StmtOnly m
+ (mkSequential DebugPointAtSequential.SuppressStmt m
(mkValSet mTry pcVar (mkInt32 g m pcMap.[innerDisposeContinuationLabel]))
generate1 )
// set the PC past the try/finally before trying to run it, to make sure we only run it once
@@ -430,7 +430,7 @@ let ConvertSequenceExprToObject g amap overallExpr =
dispose1
// set the PC past the try/finally before trying to run it, to make sure we only run it once
(mkLabelled m innerDisposeContinuationLabel
- (mkSequential DebugPointAtSequential.StmtOnly m
+ (mkSequential DebugPointAtSequential.SuppressStmt m
(mkValSet mFinally pcVar (mkInt32 g m pcMap.[currentDisposeContinuationLabel]))
(mkCompGenSequential m
compensation
@@ -478,17 +478,17 @@ let ConvertSequenceExprToObject g amap overallExpr =
// Restriction: compilation of sequence expressions containing non-toplevel constrained generic functions is not supported
when bind.Var.IsCompiledAsTopLevel || not (IsGenericValWithGenericConstraints g bind.Var) ->
- let resBody = ConvertSeqExprCode false isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel bodyExpr
- match resBody with
- | Some res2 ->
+ let resBodyOpt = ConvertSeqExprCode false isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel bodyExpr
+ match resBodyOpt with
+ | Some resBody ->
if bind.Var.IsCompiledAsTopLevel then
- Some (RepresentBindingsAsLifted (mkLetBind m bind) res2)
- elif not (res2.asyncVars.FreeLocals.Contains(bind.Var)) then
+ Some (RepresentBindingsAsLifted (mkLetBind m bind) resBody)
+ elif not (resBody.asyncVars.FreeLocals.Contains(bind.Var)) then
// printfn "found state variable %s" bind.Var.DisplayName
- Some (RepresentBindingAsLocal bind res2 m)
+ Some (RepresentBindingAsLocal bind resBody m)
else
// printfn "found state variable %s" bind.Var.DisplayName
- Some (RepresentBindingAsStateMachineLocal bind res2 m)
+ Some (RepresentBindingAsStateMachineLocal bind resBody m)
| None ->
None
@@ -526,31 +526,33 @@ let ConvertSequenceExprToObject g amap overallExpr =
// transferred to the r.h.s. are not yet compiled.
//
// TODO: remove this limitation
- | Expr.Match (spBind, exprm, pt, targets, m, ty) when targets |> Array.forall (fun (TTarget(vs, _e, _spTarget)) -> isNil vs) ->
- // lower all the targets. abandon if any fail to lower
+ | Expr.Match (spBind, exprm, pt, targets, m, ty) ->
// lower all the targets. abandon if any fail to lower
- let tglArray = targets |> Array.map (fun (TTarget(_vs, targetExpr, _spTarget)) -> ConvertSeqExprCode false isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel targetExpr)
+ let tglArray = targets |> Array.map (fun (TTarget(_vs, targetExpr, _spTarget, _)) -> ConvertSeqExprCode false isTailCall noDisposeContinuationLabel currentDisposeContinuationLabel targetExpr)
if tglArray |> Array.forall Option.isSome then
let tglArray = Array.map Option.get tglArray
let tgl = Array.toList tglArray
let labs = tgl |> List.collect (fun res -> res.entryPoints)
- let (asyncVars, _) =
- ((emptyFreeVars, false), Array.zip targets tglArray)
- ||> Array.fold (fun (fvs, seenLabel) ((TTarget(_vs, e, _spTarget)), res) ->
- if seenLabel then unionFreeVars fvs (freeInExpr CollectLocals e), true
- else res.asyncVars, not res.entryPoints.IsEmpty)
+ let asyncVars =
+ (emptyFreeVars, Array.zip targets tglArray)
+ ||> Array.fold (fun fvs ((TTarget(_vs, _, _spTarget, _)), res) ->
+ if res.entryPoints.IsEmpty then fvs else unionFreeVars fvs res.asyncVars)
- let stateVars = tgl |> List.collect (fun res -> res.stateVars)
+ let stateVars =
+ (targets, tglArray) ||> Array.zip |> Array.toList |> List.collect (fun (TTarget(vs, _, _, _), res) ->
+ let stateVars = vs |> List.filter (fun v -> res.asyncVars.FreeLocals.Contains(v)) |> List.map mkLocalValRef
+ stateVars @ res.stateVars)
let significantClose = tgl |> List.exists (fun res -> res.significantClose)
Some { phase2 = (fun ctxt ->
let gtgs, disposals, checkDisposes =
(Array.toList targets, tgl)
- ||> List.map2 (fun (TTarget(vs, _, spTarget)) res ->
+ ||> List.map2 (fun (TTarget(vs, _, spTarget, _)) res ->
+ let flags = vs |> List.map (fun v -> res.asyncVars.FreeLocals.Contains(v))
let generate, dispose, checkDispose = res.phase2 ctxt
- let gtg = TTarget(vs, generate, spTarget)
+ let gtg = TTarget(vs, generate, spTarget, Some flags)
gtg, dispose, checkDispose)
|> List.unzip3
let generate = primMkMatch (spBind, exprm, pt, Array.ofList gtgs, m, ty)
@@ -596,9 +598,9 @@ let ConvertSequenceExprToObject g amap overallExpr =
let label = IL.generateCodeLabel()
Some { phase2 = (fun (pcVar, _currv, nextVar, pcMap) ->
let generate =
- mkCompGenSequential m
+ mkSequential DebugPointAtSequential.SuppressStmt m
(mkValSet m pcVar (mkInt32 g m pcMap.[label]))
- (mkSequential DebugPointAtSequential.Both m
+ (mkCompGenSequential m
(mkAddrSet m nextVar arbitrarySeqExpr)
(mkCompGenSequential m
(Expr.Op (TOp.Return, [], [mkTwo g m], m))
@@ -812,7 +814,6 @@ let ConvertSequenceExprToObject g amap overallExpr =
None
| _ -> None
-
/// Build the 'test and dispose' part of a 'use' statement
let BuildDisposableCleanup tcVal (g: TcGlobals) infoReader m (v: Val) =
let disposeMethod =
@@ -873,7 +874,7 @@ let LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr =
let res2 = ConvertSeqExprCode false isTailcall e2
match res1, res2 with
| Result.Ok (_, e1R), Result.Ok (closed2, e2R) ->
- let exprR = mkSequential DebugPointAtSequential.Both m e1R e2R
+ let exprR = mkSequential DebugPointAtSequential.SuppressNeither m e1R e2R
Result.Ok (closed2, exprR)
| Result.Error msg, _ | _, Result.Error msg -> Result.Error msg
@@ -958,10 +959,10 @@ let LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr =
| Expr.Match (spBind, exprm, pt, targets, m, ty) ->
// lower all the targets. abandon if any fail to lower
let resTargets =
- targets |> Array.map (fun (TTarget(vs, targetExpr, spTarget)) ->
+ targets |> Array.map (fun (TTarget(vs, targetExpr, spTarget, flags)) ->
match ConvertSeqExprCode false false targetExpr with
| Result.Ok (_, targetExprR) ->
- Result.Ok (TTarget(vs, targetExprR, spTarget))
+ Result.Ok (TTarget(vs, targetExprR, spTarget, flags))
| Result.Error msg -> Result.Error msg )
if resTargets |> Array.forall (function Result.Ok _ -> true | _ -> false) then
let tglArray = Array.map (function Result.Ok v -> v | _ -> failwith "unreachable") resTargets
diff --git a/src/fsharp/LowerCallsAndSeqs.fsi b/src/fsharp/LowerCallsAndSeqs.fsi
index 10a5e26550a..f676fdc7533 100644
--- a/src/fsharp/LowerCallsAndSeqs.fsi
+++ b/src/fsharp/LowerCallsAndSeqs.fsi
@@ -22,6 +22,6 @@ val LowerImplFile: g: TcGlobals -> assembly: TypedImplFile -> TypedImplFile
/// free variables of the sequence expression.
val ConvertSequenceExprToObject: g: TcGlobals -> amap: ImportMap -> overallExpr: Expr -> (ValRef * ValRef * ValRef * ValRef list * Expr * Expr * Expr * TType * range) option
-val IsPossibleSequenceExpr: g: TcGlobals -> overallExpr: Expr -> bool
+val IsPossibleSequenceExpr: g: TcGlobals -> overallExpr: Expr -> bool
val LowerComputedListOrArrayExpr: tcVal: ConstraintSolver.TcValF -> g: TcGlobals -> amap: ImportMap -> Expr -> Expr option
diff --git a/src/fsharp/LowerStateMachines.fs b/src/fsharp/LowerStateMachines.fs
new file mode 100644
index 00000000000..55043a19bdf
--- /dev/null
+++ b/src/fsharp/LowerStateMachines.fs
@@ -0,0 +1,906 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+module internal FSharp.Compiler.LowerStateMachines
+
+open Internal.Utilities.Collections
+open Internal.Utilities.Library
+open Internal.Utilities.Library.Extras
+open FSharp.Compiler.AbstractIL
+open FSharp.Compiler.AbstractIL.IL
+open FSharp.Compiler.ErrorLogger
+open FSharp.Compiler.TcGlobals
+open FSharp.Compiler.Syntax
+open FSharp.Compiler.Syntax.PrettyNaming
+open FSharp.Compiler.TypedTree
+open FSharp.Compiler.TypedTreeBasics
+open FSharp.Compiler.TypedTreeOps
+
+let mkLabelled m l e = mkCompGenSequential m (Expr.Op (TOp.Label l, [], [], m)) e
+
+type StateMachineConversionFirstPhaseResult =
+ {
+ /// Represents the expanded expression prior to decisions about labels
+ phase1: Expr
+
+ /// The second phase of the transformation. It is run after all code labels and their mapping to program counters have been determined
+ /// after the first phase.
+ phase2: (Map -> Expr)
+
+ /// The labels allocated for this portion of the computation
+ entryPoints: int list
+
+ /// The state variables allocated for one portion of the sequence expression (i.e. the local let-bound variables which become state variables)
+ stateVars: ValRef list
+
+ /// All this values get represented via the 'this' pointer
+ thisVars: ValRef list
+
+ /// The vars captured by the non-synchronous resumable path
+ resumableVars: FreeVars
+ }
+
+#if DEBUG
+let sm_verbose = try System.Environment.GetEnvironmentVariable("FSharp_StateMachineVerbose") <> null with _ -> false
+#else
+let sm_verbose = false
+#endif
+
+let rec (|OptionalResumeAtExpr|) g expr =
+ match expr with
+ | IfUseResumableStateMachinesExpr g (OptionalResumeAtExpr g res, _) -> res
+ | Expr.Sequential(ResumeAtExpr g pcExpr, codeExpr, NormalSeq, _, _m) -> (Some pcExpr, codeExpr)
+ | _ -> (None, expr)
+
+/// Implement a decision to represent a 'let' binding as a non-escaping local variable (rather than a state machine variable)
+let RepresentBindingAsTopLevelOrLocal (bind: Binding) (res2: StateMachineConversionFirstPhaseResult) m =
+ if sm_verbose then
+ printfn "LowerStateMachine: found local variable %s" bind.Var.DisplayName
+
+ { res2 with
+ phase1 = mkLetBind m bind res2.phase1
+ phase2 = (fun ctxt -> mkLetBind m bind (res2.phase2 ctxt)) }
+
+/// Implement a decision to represent a 'let' binding as the 'this' pointer of the state machine,
+/// because it is rebinding the 'this' variable
+let RepresentBindingAsThis (bind: Binding) (res2: StateMachineConversionFirstPhaseResult) _m =
+ if sm_verbose then
+ printfn "LowerStateMachine: found local variable %s" bind.Var.DisplayName
+
+ { res2 with
+ thisVars = mkLocalValRef bind.Var :: res2.thisVars
+ // Drop the let binding on the floor as it is only rebinding the 'this' variable
+ phase1 = res2.phase1
+ phase2 = res2.phase2 }
+
+/// Implement a decision to represent a 'let' binding as a state machine variable
+let RepresentBindingAsStateVar g (bind: Binding) (resBody: StateMachineConversionFirstPhaseResult) m =
+ if sm_verbose then
+ printfn "LowerStateMachine: found state variable %s" bind.Var.DisplayName
+
+ let (TBind(v, e, sp)) = bind
+ let sp, spm =
+ match sp with
+ | DebugPointAtBinding.Yes m -> DebugPointAtSequential.SuppressNeither, m
+ | _ -> DebugPointAtSequential.SuppressStmt, e.Range
+ let vref = mkLocalValRef v
+ { resBody with
+ phase1 = mkSequential sp m (mkValSet spm vref e) resBody.phase1
+ phase2 = (fun ctxt ->
+ let generateBody = resBody.phase2 ctxt
+ let generate =
+ mkSequential sp m
+ (mkValSet spm vref e)
+ // Within all resumable code, a return value of 'true' indicates success/completion path, when we can clear
+ // state machine locals.
+ (if typeEquiv g (tyOfExpr g generateBody) g.bool_ty then
+ mkCond DebugPointAtBinding.NoneAtInvisible DebugPointForTarget.No m g.bool_ty generateBody
+ (mkCompGenSequential m
+ (mkValSet m vref (mkDefault (m, vref.Type)))
+ (mkTrue g m))
+ (mkFalse g m)
+ else
+ generateBody)
+ generate )
+ stateVars = vref :: resBody.stateVars }
+
+let isExpandVar g (v: Val) =
+ isReturnsResumableCodeTy g v.TauType &&
+ not v.IsCompiledAsTopLevel
+
+// We allow a prefix of bindings prior to the state machine, e.g.
+// task { .. }
+// becomes
+// let builder@ = task
+// ....
+let isStateMachineBindingVar g (v: Val) =
+ isExpandVar g v ||
+ (let nm = v.LogicalName
+ (nm.StartsWith "builder@"
+ || (v.BaseOrThisInfo = MemberThisVal)) &&
+ not v.IsCompiledAsTopLevel)
+
+type env =
+ {
+ ResumableCodeDefns: ValMap
+ TemplateStructTy: TType option
+ //MachineAddrExpr: Expr option
+ }
+
+ static member Empty =
+ { ResumableCodeDefns = ValMap.Empty
+ TemplateStructTy = None
+ //MachineAddrExpr = None
+ }
+
+/// Detect prefix of expanded, optimized state machine expressions
+/// This is run on every expression during codegen
+let rec IsStateMachineExpr g overallExpr =
+ match overallExpr with
+ // 'let' binding of initial code
+ | Expr.Let (defn, bodyExpr, m, _) when isStateMachineBindingVar g defn.Var ->
+ match IsStateMachineExpr g bodyExpr with
+ | None -> None
+ | Some altExpr as r ->
+ match altExpr with
+ | None -> r
+ | Some e -> Some (Some (mkLetBind m defn e))
+ // Recognise 'if __useResumableCode ...'
+ | IfUseResumableStateMachinesExpr g (thenExpr, elseExpr) ->
+ match IsStateMachineExpr g thenExpr with
+ | None -> None
+ | Some _ -> Some (Some elseExpr)
+ | StructStateMachineExpr g _ -> Some None
+ | _ -> None
+
+type LoweredStateMachine =
+ LoweredStateMachine of
+ templateStructTy: TType *
+ dataTy: TType *
+ stateVars: ValRef list *
+ thisVars: ValRef list *
+ moveNext: (Val * Expr) *
+ setStateMachine: (Val * Val * Expr) *
+ afterCode: (Val * Expr)
+
+type LoweredStateMachineResult =
+ /// A state machine was recognised and was compilable
+ | Lowered of LoweredStateMachine
+
+ /// A state machine was recognised and was not compilable and an alternative is available
+ | UseAlternative of message: string * alternativeExpr: Expr
+
+ /// A state machine was recognised and was not compilable and no alternative is available
+ | NoAlternative of message: string
+
+ /// The construct was not a state machine
+ | NotAStateMachine
+
+/// Used to scope the action of lowering a state machine expression
+type LowerStateMachine(g: TcGlobals) =
+
+ let mutable pcCount = 0
+ let genPC() =
+ pcCount <- pcCount + 1
+ pcCount
+
+ // Record definitions for any resumable code
+ let rec BindResumableCodeDefinitions (env: env) expr =
+
+ match expr with
+ // Bind 'let __expand_ABC = bindExpr in bodyExpr'
+ | Expr.Let (defn, bodyExpr, _, _) when isStateMachineBindingVar g defn.Var ->
+ if sm_verbose then printfn "binding %A --> %A..." defn.Var defn.Expr
+ let envR = { env with ResumableCodeDefns = env.ResumableCodeDefns.Add defn.Var defn.Expr }
+ BindResumableCodeDefinitions envR bodyExpr
+
+ // Eliminate 'if __useResumableCode ...'
+ | IfUseResumableStateMachinesExpr g (thenExpr, _) ->
+ if sm_verbose then printfn "eliminating 'if __useResumableCode...'"
+ BindResumableCodeDefinitions env thenExpr
+
+ | _ ->
+ (env, expr)
+
+ let rec TryReduceApp (env: env) expr (args: Expr list) =
+ if isNil args then None else
+ match expr with
+ | Expr.TyLambda _
+ | Expr.Lambda _ ->
+ let macroTypars, macroParamsCurried, macroBody, _rty = stripTopLambda (expr, tyOfExpr g expr)
+ let m = macroBody.Range
+ if not (isNil macroTypars) then
+ //warning(Error(FSComp.SR.stateMachineMacroTypars(), m))
+ None
+ else
+ let macroParams = List.concat macroParamsCurried
+ let macroVal2 = mkLambdas m macroTypars macroParams (macroBody, tyOfExpr g macroBody)
+ if args.Length < macroParams.Length then
+ //warning(Error(FSComp.SR.stateMachineMacroUnderapplied(), m))
+ None
+ else
+ let nowArgs, laterArgs = List.splitAt macroParams.Length args
+ let expandedExpr = MakeApplicationAndBetaReduce g (macroVal2, (tyOfExpr g macroVal2), [], nowArgs, m)
+ if sm_verbose then printfn "reduced application f = %A nowArgs= %A --> %A" macroVal2 nowArgs expandedExpr
+ if isNil laterArgs then
+ Some expandedExpr
+ else
+ if sm_verbose then printfn "application was partial, reducing further args %A" laterArgs
+ TryReduceApp env expandedExpr laterArgs
+
+ | NewDelegateExpr g (_, macroParamsCurried, macroBody, _, _) ->
+ let m = expr.Range
+ let macroParams = List.concat macroParamsCurried
+ let macroVal2 = mkLambdas m [] macroParams (macroBody, tyOfExpr g macroBody)
+ if args.Length < macroParams.Length then
+ //warning(Error(FSComp.SR.stateMachineMacroUnderapplied(), m))
+ None
+ else
+ let nowArgs, laterArgs = List.splitAt macroParams.Length args
+ let expandedExpr = MakeApplicationAndBetaReduce g (macroVal2, (tyOfExpr g macroVal2), [], nowArgs, m)
+ if sm_verbose then printfn "reduced application f = %A nowArgs= %A --> %A" macroVal2 nowArgs expandedExpr
+ if isNil laterArgs then
+ Some expandedExpr
+ else
+ if sm_verbose then printfn "application was partial, reducing further args %A" laterArgs
+ TryReduceApp env expandedExpr laterArgs
+
+ | Expr.Let (bind, bodyExpr, m, _) ->
+ match TryReduceApp env bodyExpr args with
+ | Some bodyExpr2 -> Some (mkLetBind m bind bodyExpr2)
+ | None -> None
+
+ | Expr.LetRec (binds, bodyExpr, m, _) ->
+ match TryReduceApp env bodyExpr args with
+ | Some bodyExpr2 -> Some (mkLetRecBinds m binds bodyExpr2)
+ | None -> None
+
+ | Expr.Sequential (x1, bodyExpr, sp, ty, m) ->
+ match TryReduceApp env bodyExpr args with
+ | Some bodyExpr2 -> Some (Expr.Sequential (x1, bodyExpr2, sp, ty, m))
+ | None -> None
+
+ // This construct arises from the 'mkDefault' in the 'Throw' case of an incomplete pattern match
+ | Expr.Const (Const.Zero, m, ty) ->
+ Some (Expr.Const (Const.Zero, m, ty))
+
+ | Expr.Match (spBind, exprm, dtree, targets, m, ty) ->
+ let mutable newTyOpt = None
+ let targets2 =
+ targets |> Array.choose (fun (TTarget(vs, targetExpr, spTarget, flags)) ->
+ // Incomplete exception matching expressions give rise to targets with I_throw.
+ // and System.Runtime.ExceptionServices.ExceptionDispatchInfo::Throw(...)
+ //
+ // Keep these in the residue.
+ //
+ // In theory the type of the expression should be adjusted but code generation doesn't record the
+ // type in the IL
+ let targetExpr2Opt =
+ match targetExpr, newTyOpt with
+ | Expr.Op (TOp.ILAsm ([ AbstractIL.IL.I_throw ], [_oldTy]), a, b, c), Some newTy ->
+ let targetExpr2 = Expr.Op (TOp.ILAsm ([ AbstractIL.IL.I_throw ], [newTy]), a, b, c)
+ Some targetExpr2
+ | Expr.Sequential ((Expr.Op (TOp.ILCall ( _, _, _, _, _, _, _, ilMethodRef, _, _, _), _, _, _) as e1), Expr.Const (Const.Zero, m, _oldTy), a, b, c), Some newTy when ilMethodRef.Name = "Throw" ->
+ let targetExpr2 = Expr.Sequential (e1, Expr.Const (Const.Zero, m, newTy), a, b, c)
+ Some targetExpr2
+ | _ ->
+
+ match TryReduceApp env targetExpr args with
+ | Some targetExpr2 ->
+ newTyOpt <- Some (tyOfExpr g targetExpr2)
+ Some targetExpr2
+ | None ->
+ None
+ match targetExpr2Opt with
+ | Some targetExpr2 -> Some (TTarget(vs, targetExpr2, spTarget, flags))
+ | None -> None)
+ if targets2.Length = targets.Length then
+ Some (Expr.Match (spBind, exprm, dtree, targets2, m, ty))
+ else
+ None
+
+ | WhileExpr (sp1, sp2, guardExpr, bodyExpr, m) ->
+ match TryReduceApp env bodyExpr args with
+ | Some bodyExpr2 -> Some (mkWhile g (sp1, sp2, guardExpr, bodyExpr2, m))
+ | None -> None
+
+ | TryFinallyExpr (sp1, sp2, ty, bodyExpr, compensation, m) ->
+ match TryReduceApp env bodyExpr args with
+ | Some bodyExpr2 -> Some (mkTryFinally g (bodyExpr2, compensation, m, ty, sp1, sp2))
+ | None -> None
+
+ | TryWithExpr (spTry, spWith, resTy, bodyExpr, filterVar, filterExpr, handlerVar, handlerExpr, m) ->
+ match TryReduceApp env bodyExpr args with
+ | Some bodyExpr2 -> Some (mkTryWith g (bodyExpr2, filterVar, filterExpr, handlerVar, handlerExpr, m, resTy, spTry, spWith))
+ | None -> None
+
+ | _ ->
+ None
+
+ // Apply a single expansion of resumable code at the outermost position in an arbitrary expression
+ let rec TryReduceExpr (env: env) expr args remake =
+ if sm_verbose then printfn "expanding defns and reducing %A..." expr
+ //if sm_verbose then printfn "checking %A for possible resumable code application..." expr
+ match expr with
+ // defn --> [expand_code]
+ | Expr.Val (defnRef, _, _) when env.ResumableCodeDefns.ContainsVal defnRef.Deref ->
+ let defn = env.ResumableCodeDefns.[defnRef.Deref]
+ if sm_verbose then printfn "found resumable code %A --> %A" defnRef defn
+ // Expand the resumable code definition
+ match TryReduceApp env defn args with
+ | Some expandedExpr ->
+ if sm_verbose then printfn "expanded resumable code %A --> %A..." defnRef expandedExpr
+ Some expandedExpr
+ | None ->
+ Some (remake defn)
+
+ // defn.Invoke x --> let arg = x in [defn][arg/x]
+ | ResumableCodeInvoke g (_, f, args2, _, rebuild) ->
+ if sm_verbose then printfn "found delegate invoke in possible reduction, f = %A, args now %A..." f (args2 @ args)
+ TryReduceExpr env f (args2 @ args) (fun f2 -> remake (rebuild (f2, args2)))
+
+ // defn x --> let arg = x in [defn][arg/x]
+ | Expr.App (f, _fty, _tyargs, args2, _m) ->
+ if sm_verbose then printfn "found function invoke in possible reduction, f = %A, args now %A..." f (args2 @ args)
+ TryReduceExpr env f (args2 @ args) (fun f2 -> remake (Expr.App (f2, _fty, _tyargs, args2, _m)))
+
+ | _ ->
+ //let (env, expr) = BindResumableCodeDefinitions env expr
+ match TryReduceApp env expr args with
+ | Some expandedExpr ->
+ if sm_verbose then printfn "reduction = %A, args = %A --> %A..." expr args expandedExpr
+ Some expandedExpr
+ | None ->
+ None
+
+ // Repeated top-down rewrite
+ let makeRewriteEnv (env: env) =
+ { PreIntercept = Some (fun cont e -> match TryReduceExpr env e [] id with Some e2 -> Some (cont e2) | None -> None)
+ PostTransform = (fun _ -> None)
+ PreInterceptBinding = None
+ IsUnderQuotations=true }
+
+ let ConvertStateMachineLeafExpression (env: env) expr =
+ if sm_verbose then printfn "ConvertStateMachineLeafExpression for %A..." expr
+ expr |> RewriteExpr (makeRewriteEnv env)
+
+ let ConvertStateMachineLeafDecisionTree (env: env) expr =
+ if sm_verbose then printfn "ConvertStateMachineLeafDecisionTree for %A..." expr
+ expr |> RewriteDecisionTree (makeRewriteEnv env)
+
+ /// Repeatedly find outermost expansion definitions and apply outermost expansions
+ let rec RepeatBindAndApplyOuterDefinitions (env: env) expr =
+ if sm_verbose then printfn "RepeatBindAndApplyOuterDefinitions for %A..." expr
+ let env2, expr2 = BindResumableCodeDefinitions env expr
+ match TryReduceExpr env2 expr2 [] id with
+ | Some res -> RepeatBindAndApplyOuterDefinitions env2 res
+ | None -> env2, expr2
+
+ // Detect a state machine with a single method override
+ let (|ExpandedStateMachineInContext|_|) inputExpr =
+ // All expanded resumable code state machines e.g. 'task { .. }' begin with a bind of @builder or 'defn'
+ let env, expr = BindResumableCodeDefinitions env.Empty inputExpr
+ match expr with
+ | StructStateMachineExpr g
+ (dataTy,
+ (moveNextThisVar, moveNextBody),
+ (setStateMachineThisVar, setStateMachineStateVar, setStateMachineBody),
+ (afterCodeThisVar, afterCodeBody)) ->
+ let templateStructTy = g.mk_ResumableStateMachine_ty dataTy
+ let env = { env with TemplateStructTy = Some templateStructTy }
+ if sm_verbose then printfn "Found struct machine..."
+ if sm_verbose then printfn "Found struct machine jump table call..."
+ let setStateMachineBodyR = ConvertStateMachineLeafExpression env setStateMachineBody
+ let afterCodeBodyR = ConvertStateMachineLeafExpression env afterCodeBody
+ let remake2 (moveNextExprR, stateVars, thisVars) =
+ if sm_verbose then
+ printfn "----------- AFTER REWRITE moveNextExprWithJumpTable ----------------------"
+ printfn "%s" (DebugPrint.showExpr g moveNextExprR)
+ printfn "----------- AFTER REWRITE setStateMachineBodyR ----------------------"
+ printfn "%s" (DebugPrint.showExpr g setStateMachineBodyR)
+ printfn "----------- AFTER REWRITE afterCodeBodyR ----------------------"
+ printfn "%s" (DebugPrint.showExpr g afterCodeBodyR)
+ LoweredStateMachine
+ (templateStructTy, dataTy, stateVars, thisVars,
+ (moveNextThisVar, moveNextExprR),
+ (setStateMachineThisVar, setStateMachineStateVar, setStateMachineBodyR),
+ (afterCodeThisVar, afterCodeBodyR))
+ Some (env, remake2, moveNextBody)
+ | _ ->
+ None
+
+ // A utility to add a jump table an expression
+ let addPcJumpTable m (pcs: int list) (pc2lab: Map) pcExpr expr =
+ if pcs.IsEmpty then
+ expr
+ else
+ let initLabel = IL.generateCodeLabel()
+ let mbuilder = new MatchBuilder(DebugPointAtBinding.NoneAtInvisible, m )
+ let mkGotoLabelTarget lab = mbuilder.AddResultTarget(Expr.Op (TOp.Goto lab, [], [], m), DebugPointForTarget.No)
+ let dtree =
+ TDSwitch(pcExpr,
+ [ // Yield one target for each PC, where the action of the target is to goto the appropriate label
+ for pc in pcs do
+ yield mkCase(DecisionTreeTest.Const(Const.Int32 pc), mkGotoLabelTarget pc2lab.[pc]) ],
+ // The default is to go to pcInit
+ Some(mkGotoLabelTarget initLabel),
+ m)
+
+ let table = mbuilder.Close(dtree, m, g.int_ty)
+ mkCompGenSequential m table (mkLabelled m initLabel expr)
+
+ /// Detect constructs allowed in state machines
+ let rec ConvertResumableCode env (pcValInfo: ((Val * Expr) * Expr) option) expr : Result =
+ if sm_verbose then
+ printfn "---------ConvertResumableCode-------------------"
+ printfn "%s" (DebugPrint.showExpr g expr)
+ printfn "---------"
+
+ let env, expr = RepeatBindAndApplyOuterDefinitions env expr
+
+ if sm_verbose then
+ printfn "After RepeatBindAndApplyOuterDefinitions:\n%s" (DebugPrint.showExpr g expr)
+ printfn "---------"
+
+ // Detect the different permitted constructs in the expanded state machine
+ let res =
+ match expr with
+ | ResumableCodeInvoke g (_, _, _, m, _) ->
+ Result.Error (FSComp.SR.reprResumableCodeInvokeNotReduced(m.ToShortString()))
+
+ // Eliminate 'if __useResumableCode ...' within.
+ | IfUseResumableStateMachinesExpr g (thenExpr, _) ->
+ ConvertResumableCode env pcValInfo thenExpr
+
+ | ResumableEntryMatchExpr g (noneBranchExpr, someVar, someBranchExpr, _rebuild) ->
+ ConvertResumableEntry env pcValInfo (noneBranchExpr, someVar, someBranchExpr, _rebuild)
+
+ | ResumeAtExpr g pcExpr ->
+ ConvertResumableResumeAt env (pcExpr, expr.Range)
+
+ // The expanded code for state machines may use sequential binding and sequential execution.
+ //
+ // let __stack_step = e1 in e2
+ // e1; e2
+ //
+ // A binding 'let .. = ... in ... ' is considered part of the state machine logic
+ // if it uses a binding variable starting with '__stack_*'.
+ // If this case 'e1' becomes part of the state machine too.
+ | SequentialResumableCode g (e1, e2, _m, recreate) ->
+ ConvertResumableSequential env pcValInfo (e1, e2, _m, recreate)
+
+ // The expanded code for state machines may use while loops...
+ | WhileExpr (sp1, sp2, guardExpr, bodyExpr, m) ->
+ ConvertResumableWhile env pcValInfo (sp1, sp2, guardExpr, bodyExpr, m)
+
+ // The expanded code for state machines should not normally contain try/finally as any resumptions will repeatedly execute the finally.
+ // However we include the synchronous version of the construct here for completeness.
+ | TryFinallyExpr (sp1, sp2, ty, e1, e2, m) ->
+ ConvertResumableTryFinally env pcValInfo (sp1, sp2, ty, e1, e2, m)
+
+ // The expanded code for state machines may use for loops, however the
+ // body must be synchronous.
+ | ForLoopExpr (sp1, sp2, e1, e2, v, e3, m) ->
+ ConvertResumableFastIntegerForLoop env pcValInfo (sp1, sp2, e1, e2, v, e3, m)
+
+ // The expanded code for state machines may use try/with....
+ | TryWithExpr (spTry, spWith, resTy, bodyExpr, filterVar, filterExpr, handlerVar, handlerExpr, m) ->
+ ConvertResumableTryWith env pcValInfo (spTry, spWith, resTy, bodyExpr, filterVar, filterExpr, handlerVar, handlerExpr, m)
+
+ // control-flow match
+ | Expr.Match (spBind, exprm, dtree, targets, m, ty) ->
+ ConvertResumableMatch env pcValInfo (spBind, exprm, dtree, targets, m, ty)
+
+ // Non-control-flow let binding can appear as part of state machine. The body is considered state-machine code,
+ // the expression being bound is not.
+ | Expr.Let (bind, bodyExpr, m, _)
+ // Restriction: compilation of sequence expressions containing non-toplevel constrained generic functions is not supported
+ when bind.Var.IsCompiledAsTopLevel || not (IsGenericValWithGenericConstraints g bind.Var) ->
+ ConvertResumableLet env pcValInfo (bind, bodyExpr, m)
+
+ | Expr.LetRec _ ->
+ Result.Error (FSComp.SR.reprResumableCodeContainsLetRec())
+
+ // Arbitrary expression
+ | _ ->
+ let exprR = ConvertStateMachineLeafExpression env expr
+ { phase1 = exprR
+ phase2 = (fun _ctxt -> exprR)
+ entryPoints = []
+ stateVars = []
+ thisVars = []
+ resumableVars = emptyFreeVars }
+ |> Result.Ok
+
+ if sm_verbose then
+ match res with
+ | Result.Ok res ->
+ printfn "-------------------"
+ printfn "Phase 1 Done for %s" (DebugPrint.showExpr g res.phase1)
+ printfn "Phase 1 Done, resumableVars = %A" (res.resumableVars.FreeLocals |> Zset.elements |> List.map (fun v -> v.CompiledName(g.CompilerGlobalState)) |> String.concat ",")
+ printfn "Phase 1 Done, stateVars = %A" (res.stateVars |> List.map (fun v -> v.CompiledName(g.CompilerGlobalState)) |> String.concat ",")
+ printfn "Phase 1 Done, thisVars = %A" (res.thisVars |> List.map (fun v -> v.CompiledName(g.CompilerGlobalState)) |> String.concat ",")
+ printfn "-------------------"
+ | Result.Error msg->
+ printfn "Phase 1 failed: %s" msg
+ printfn "Phase 1 failed for %s" (DebugPrint.showExpr g expr)
+ res
+
+ and ConvertResumableEntry env pcValInfo (noneBranchExpr, someVar, someBranchExpr, _rebuild) =
+ if sm_verbose then printfn "ResumableEntryMatchExpr"
+ // printfn "found sequential"
+ let reenterPC = genPC()
+ let envSome = { env with ResumableCodeDefns = env.ResumableCodeDefns.Add someVar (mkInt g someVar.Range reenterPC) }
+ let resNone = ConvertResumableCode env pcValInfo noneBranchExpr
+ let resSome = ConvertResumableCode envSome pcValInfo someBranchExpr
+
+ match resNone, resSome with
+ | Result.Ok resNone, Result.Ok resSome ->
+ let resumableVars = unionFreeVars (freeInExpr CollectLocals resNone.phase1) resSome.resumableVars
+ let m = someBranchExpr.Range
+ let recreate reenterLabOpt e1 e2 =
+ let lab = (match reenterLabOpt with Some l -> l | _ -> IL.generateCodeLabel())
+ mkCond DebugPointAtBinding.NoneAtSticky DebugPointForTarget.No m (tyOfExpr g noneBranchExpr) (mkFalse g m) (mkLabelled m lab e1) e2
+ { phase1 = recreate None resNone.phase1 resSome.phase1
+ phase2 = (fun ctxt ->
+ let generate2 = resSome.phase2 ctxt
+ let generate1 = resNone.phase2 ctxt
+ let generate = recreate (Some ctxt.[reenterPC]) generate1 generate2
+ generate)
+ entryPoints= resSome.entryPoints @ [reenterPC] @ resNone.entryPoints
+ stateVars = resSome.stateVars @ resNone.stateVars
+ thisVars = resSome.thisVars @ resNone.thisVars
+ resumableVars = resumableVars }
+ |> Result.Ok
+ | Result.Error err, _ | _, Result.Error err -> Result.Error err
+
+ and ConvertResumableResumeAt env (pcExpr , m)=
+ if sm_verbose then printfn "ResumeAtExpr"
+ // Macro-evaluate the pcExpr
+ let pcExprVal = ConvertStateMachineLeafExpression env pcExpr
+ match pcExprVal with
+ | Int32Expr contIdPC ->
+ let recreate contLabOpt =
+ Expr.Op (TOp.Goto (match contLabOpt with Some l -> l | _ -> IL.generateCodeLabel()), [], [], m)
+
+ { phase1 = recreate None
+ phase2 = (fun ctxt ->
+ let generate = recreate (Some ctxt.[contIdPC])
+ generate)
+ entryPoints = []
+ stateVars = []
+ thisVars = []
+ resumableVars = emptyFreeVars }
+ |> Result.Ok
+ | _ ->
+ Result.Error(FSComp.SR.reprResumableCodeContainsDynamicResumeAtInBody())
+
+ and ConvertResumableSequential env pcValInfo (e1, e2, _m, recreate) =
+ if sm_verbose then printfn "SequentialResumableCode"
+ // printfn "found sequential"
+ let res1 = ConvertResumableCode env pcValInfo e1
+ let res2 = ConvertResumableCode env pcValInfo e2
+ match res1, res2 with
+ | Result.Ok res1, Result.Ok res2 ->
+ let resumableVars =
+ if res1.entryPoints.IsEmpty then
+ // res1 is synchronous
+ res2.resumableVars
+ else
+ // res1 is not synchronous. All of 'e2' is needed after resuming at any of the labels
+ unionFreeVars res1.resumableVars (freeInExpr CollectLocals res2.phase1)
+
+ { phase1 = recreate res1.phase1 res2.phase1
+ phase2 = (fun ctxt ->
+ let generate1 = res1.phase2 ctxt
+ let generate2 = res2.phase2 ctxt
+ let generate = recreate generate1 generate2
+ generate)
+ entryPoints= res1.entryPoints @ res2.entryPoints
+ stateVars = res1.stateVars @ res2.stateVars
+ thisVars = res1.thisVars @ res2.thisVars
+ resumableVars = resumableVars }
+ |> Result.Ok
+ | Result.Error err, _ | _, Result.Error err -> Result.Error err
+
+ and ConvertResumableWhile env pcValInfo (sp1, sp2, guardExpr, bodyExpr, m) =
+ if sm_verbose then printfn "WhileExpr"
+
+ let resg = ConvertResumableCode env pcValInfo guardExpr
+ let resb = ConvertResumableCode env pcValInfo bodyExpr
+ match resg, resb with
+ | Result.Ok resg, Result.Ok resb ->
+ let eps = resg.entryPoints @ resb.entryPoints
+ // All free variables get captured if there are any entrypoints at all
+ let resumableVars = if eps.IsEmpty then emptyFreeVars else unionFreeVars (freeInExpr CollectLocals resg.phase1) (freeInExpr CollectLocals resb.phase1)
+ { phase1 = mkWhile g (sp1, sp2, resg.phase1, resb.phase1, m)
+ phase2 = (fun ctxt ->
+ let egR = resg.phase2 ctxt
+ let ebR = resb.phase2 ctxt
+
+ // Clear the pcVal on backward branch, causing jump tables at entry to nested try-blocks to not activate
+ let ebR2 =
+ match pcValInfo with
+ | None -> ebR
+ | Some ((pcVal, _), _) ->
+ mkCompGenThenDoSequential m
+ ebR
+ (mkValSet m (mkLocalValRef pcVal) (mkZero g m))
+
+ mkWhile g (sp1, sp2, egR, ebR2, m))
+ entryPoints= eps
+ stateVars = resg.stateVars @ resb.stateVars
+ thisVars = resg.thisVars @ resb.thisVars
+ resumableVars = resumableVars }
+ |> Result.Ok
+ | Result.Error err, _ | _, Result.Error err -> Result.Error err
+
+ and ConvertResumableTryFinally env pcValInfo (sp1, sp2, ty, e1, e2, m) =
+ if sm_verbose then printfn "TryFinallyExpr"
+ let res1 = ConvertResumableCode env pcValInfo e1
+ let res2 = ConvertResumableCode env pcValInfo e2
+ match res1, res2 with
+ | Result.Ok res1, Result.Ok res2 ->
+ let eps = res1.entryPoints @ res2.entryPoints
+ if eps.Length > 0 then
+ Result.Error (FSComp.SR.reprResumableCodeContainsResumptionInTryFinally())
+ else
+ { phase1 = mkTryFinally g (res1.phase1, res2.phase1, m, ty, sp1, sp2)
+ phase2 = (fun ctxt ->
+ let egR = res1.phase2 ctxt
+ let ebR = res2.phase2 ctxt
+ mkTryFinally g (egR, ebR, m, ty, sp1, sp2))
+ entryPoints= eps
+ stateVars = res1.stateVars @ res2.stateVars
+ thisVars = res1.thisVars @ res2.thisVars
+ resumableVars = emptyFreeVars (* eps is empty, hence synchronous, no capture *) }
+ |> Result.Ok
+ | Result.Error err, _ | _, Result.Error err -> Result.Error err
+
+ and ConvertResumableFastIntegerForLoop env pcValInfo (sp1, sp2, e1, e2, v, e3, m) =
+ if sm_verbose then printfn "ForLoopExpr"
+ let res1 = ConvertResumableCode env pcValInfo e1
+ let res2 = ConvertResumableCode env pcValInfo e2
+ let res3 = ConvertResumableCode env pcValInfo e3
+ match res1, res2, res3 with
+ | Result.Ok res1, Result.Ok res2, Result.Ok res3 ->
+ let eps = res1.entryPoints @ res2.entryPoints @ res3.entryPoints
+ if eps.Length > 0 then
+ Result.Error(FSComp.SR.reprResumableCodeContainsFastIntegerForLoop())
+ else
+ { phase1 = mkFor g (sp1, v, res1.phase1, sp2, res2.phase1, res3.phase1, m)
+ phase2 = (fun ctxt ->
+ let e1R = res1.phase2 ctxt
+ let e2R = res2.phase2 ctxt
+ let e3R = res3.phase2 ctxt
+
+ // Clear the pcVal on backward branch, causing jump tables at entry to nested try-blocks to not activate
+ let e3R2 =
+ match pcValInfo with
+ | None -> e3R
+ | Some ((pcVal, _), _) ->
+ mkCompGenThenDoSequential m
+ e3R
+ (mkValSet m (mkLocalValRef pcVal) (mkZero g m))
+
+ mkFor g (sp1, v, e1R, sp2, e2R, e3R2, m))
+ entryPoints= eps
+ stateVars = res1.stateVars @ res2.stateVars @ res3.stateVars
+ thisVars = res1.thisVars @ res2.thisVars @ res3.thisVars
+ resumableVars = emptyFreeVars (* eps is empty, hence synchronous, no capture *) }
+ |> Result.Ok
+ | Result.Error err, _, _ | _, Result.Error err, _ | _, _, Result.Error err -> Result.Error err
+
+ and ConvertResumableTryWith env pcValInfo (spTry, spWith, resTy, bodyExpr, filterVar, filterExpr, handlerVar, handlerExpr, m) =
+ if sm_verbose then printfn "TryWithExpr"
+ let resBody = ConvertResumableCode env pcValInfo bodyExpr
+ let resFilter = ConvertResumableCode env pcValInfo filterExpr
+ let resHandler = ConvertResumableCode env pcValInfo handlerExpr
+ match resBody, resFilter, resHandler with
+ | Result.Ok resBody, Result.Ok resFilter, Result.Ok resHandler ->
+ let epsNope = resFilter.entryPoints @ resHandler.entryPoints
+ if epsNope.Length > 0 then
+ Result.Error(FSComp.SR.reprResumableCodeContainsResumptionInHandlerOrFilter())
+ else
+ { phase1 = mkTryWith g (resBody.phase1, filterVar, resFilter.phase1, handlerVar, resHandler.phase1, m, resTy, spTry, spWith)
+ phase2 = (fun ctxt ->
+ // We can't jump into a try/catch block. So we jump to the start of the try/catch and add a new jump table
+ let pcsAndLabs = ctxt |> Map.toList
+ let innerPcs = resBody.entryPoints
+ if innerPcs.IsEmpty then
+ let vBodyR = resBody.phase2 ctxt
+ let filterExprR = resFilter.phase2 ctxt
+ let handlerExprR = resHandler.phase2 ctxt
+ mkTryWith g (vBodyR, filterVar, filterExprR, handlerVar, handlerExprR, m, resTy, spTry, spWith)
+ else
+ let innerPcSet = innerPcs |> Set.ofList
+ let outerLabsForInnerPcs = pcsAndLabs |> List.filter (fun (pc, _outerLab) -> innerPcSet.Contains pc) |> List.map snd
+ // generate the inner labels
+ let pcsAndInnerLabs = pcsAndLabs |> List.map (fun (pc, l) -> (pc, if innerPcSet.Contains pc then IL.generateCodeLabel() else l))
+ let innerPc2Lab = Map.ofList pcsAndInnerLabs
+
+ let vBodyR = resBody.phase2 innerPc2Lab
+ let filterExprR = resFilter.phase2 ctxt
+ let handlerExprR = resHandler.phase2 ctxt
+
+ // Add a jump table at the entry to the try
+ let vBodyRWithJumpTable =
+ match pcValInfo with
+ | None -> vBodyR
+ | Some ((_, pcValExpr), _) -> addPcJumpTable m innerPcs innerPc2Lab pcValExpr vBodyR
+ let coreExpr = mkTryWith g (vBodyRWithJumpTable, filterVar, filterExprR, handlerVar, handlerExprR, m, resTy, spTry, spWith)
+ // Place all the outer labels just before the try
+ let labelledExpr = (coreExpr, outerLabsForInnerPcs) ||> List.fold (fun e l -> mkLabelled m l e)
+
+ labelledExpr)
+ entryPoints= resBody.entryPoints @ resFilter.entryPoints @ resHandler.entryPoints
+ stateVars = resBody.stateVars @ resFilter.stateVars @ resHandler.stateVars
+ thisVars = resBody.thisVars @ resFilter.thisVars @ resHandler.thisVars
+ resumableVars = unionFreeVars resBody.resumableVars (unionFreeVars(freeInExpr CollectLocals resFilter.phase1) (freeInExpr CollectLocals resHandler.phase1)) }
+ |> Result.Ok
+ | Result.Error err, _, _ | _, Result.Error err, _ | _, _, Result.Error err -> Result.Error err
+
+ and ConvertResumableMatch env pcValInfo (spBind, exprm, dtree, targets, m, ty) =
+ if sm_verbose then printfn "MatchExpr"
+ // lower all the targets.
+ let dtreeR = ConvertStateMachineLeafDecisionTree env dtree
+ let tglArray =
+ targets |> Array.map (fun (TTarget(_vs, targetExpr, _spTarget, _)) ->
+ ConvertResumableCode env pcValInfo targetExpr)
+ match (tglArray |> Array.forall (function Result.Ok _ -> true | Result.Error _ -> false)) with
+ | true ->
+ let tglArray = tglArray |> Array.map (function Result.Ok v -> v | _ -> failwith "unreachable")
+ let tgl = tglArray |> Array.toList
+ let entryPoints = tgl |> List.collect (fun res -> res.entryPoints)
+ let resumableVars =
+ (emptyFreeVars, Array.zip targets tglArray)
+ ||> Array.fold (fun fvs ((TTarget(_vs, _, _spTarget, _)), res) ->
+ if res.entryPoints.IsEmpty then fvs else unionFreeVars fvs res.resumableVars)
+ let stateVars =
+ (targets, tglArray) ||> Array.zip |> Array.toList |> List.collect (fun (TTarget(vs, _, _, _), res) ->
+ let stateVars = vs |> List.filter (fun v -> res.resumableVars.FreeLocals.Contains(v)) |> List.map mkLocalValRef
+ stateVars @ res.stateVars)
+ let thisVars = tglArray |> Array.toList |> List.collect (fun res -> res.thisVars)
+ { phase1 =
+ let gtgs =
+ (targets, tglArray) ||> Array.map2 (fun (TTarget(vs, _, spTarget, _)) res ->
+ let flags = vs |> List.map (fun v -> res.resumableVars.FreeLocals.Contains(v))
+ TTarget(vs, res.phase1, spTarget, Some flags))
+ primMkMatch (spBind, exprm, dtreeR, gtgs, m, ty)
+
+ phase2 = (fun ctxt ->
+ let gtgs =
+ (targets, tglArray) ||> Array.map2 (fun (TTarget(vs, _, spTarget, _)) res ->
+ let flags = vs |> List.map (fun v -> res.resumableVars.FreeLocals.Contains(v))
+ TTarget(vs, res.phase2 ctxt, spTarget, Some flags))
+ let generate = primMkMatch (spBind, exprm, dtreeR, gtgs, m, ty)
+ generate)
+
+ entryPoints = entryPoints
+ stateVars = stateVars
+ resumableVars = resumableVars
+ thisVars = thisVars }
+ |> Result.Ok
+ | _ -> tglArray |> Array.find (function Result.Ok _ -> false | Result.Error _ -> true)
+
+ and ConvertResumableLet env pcValInfo (bind, bodyExpr, m) =
+ // Non-control-flow let binding can appear as part of state machine. The body is considered state-machine code,
+ // the expression being bound is not.
+ if sm_verbose then printfn "LetExpr (non-control-flow, rewrite rhs)"
+
+ // Rewrite the expression on the r.h.s. of the binding
+ let bindExpr = ConvertStateMachineLeafExpression env bind.Expr
+ let bind = mkBind bind.DebugPoint bind.Var bindExpr
+ if sm_verbose then printfn "LetExpr (non-control-flow, body)"
+
+ let resBody = ConvertResumableCode env pcValInfo bodyExpr
+
+ match resBody with
+ | Result.Ok resBody ->
+ // The isByrefTy check is an adhoc check to avoid capturing the 'this' parameter of a struct state machine
+ // You might think we could do this:
+ //
+ // let sm = &this
+ // ... await point ...
+ // ... sm ....
+ // However the 'sm' won't be set on that path.
+ if isByrefTy g bind.Var.Type &&
+ (match env.TemplateStructTy with
+ | None -> false
+ | Some ty -> typeEquiv g ty (destByrefTy g bind.Var.Type)) then
+ RepresentBindingAsThis bind resBody m
+ |> Result.Ok
+ elif bind.Var.IsCompiledAsTopLevel ||
+ not (resBody.resumableVars.FreeLocals.Contains(bind.Var)) ||
+ bind.Var.LogicalName.StartsWith stackVarPrefix then
+ if sm_verbose then printfn "LetExpr (non-control-flow, rewrite rhs, RepresentBindingAsTopLevelOrLocal)"
+ RepresentBindingAsTopLevelOrLocal bind resBody m
+ |> Result.Ok
+ else
+ if sm_verbose then printfn "LetExpr (non-control-flow, rewrite rhs, RepresentBindingAsStateVar)"
+ // printfn "found state variable %s" bind.Var.DisplayName
+ RepresentBindingAsStateVar g bind resBody m
+ |> Result.Ok
+ | Result.Error msg ->
+ Result.Error msg
+
+ member _.Apply(overallExpr, altExprOpt) =
+
+ let fallback msg =
+ match altExprOpt with
+ | None ->
+ LoweredStateMachineResult.NoAlternative msg
+ | Some altExpr ->
+ LoweredStateMachineResult.UseAlternative (msg, altExpr)
+
+ match overallExpr with
+ | ExpandedStateMachineInContext (env, remake, moveNextExpr) ->
+ let m = moveNextExpr.Range
+ match moveNextExpr with
+ | OptionalResumeAtExpr g (pcExprOpt, codeExpr) ->
+ let env, codeExprR = RepeatBindAndApplyOuterDefinitions env codeExpr
+ let frees = (freeInExpr CollectLocals overallExpr).FreeLocals
+
+ if frees |> Zset.exists (isExpandVar g) then
+ let nonfree = frees |> Zset.elements |> List.filter (isExpandVar g) |> List.map (fun v -> v.DisplayName) |> String.concat ","
+ let msg = FSComp.SR.reprResumableCodeValueHasNoDefinition(nonfree)
+ fallback msg
+ else
+ let pcExprROpt = pcExprOpt |> Option.map (ConvertStateMachineLeafExpression env)
+ let pcValInfo =
+ match pcExprROpt with
+ | None -> None
+ | Some e -> Some (mkMutableCompGenLocal e.Range "pcVal" g.int32_ty, e)
+
+ if sm_verbose then
+ printfn "Found state machine override method and code expression..."
+ printfn "----------- OVERALL EXPRESSION FOR STATE MACHINE CONVERSION ----------------------"
+ printfn "%s" (DebugPrint.showExpr g overallExpr)
+ printfn "----------- INPUT TO STATE MACHINE CONVERSION ----------------------"
+ printfn "%s" (DebugPrint.showExpr g codeExpr)
+ printfn "----------- START STATE MACHINE CONVERSION ----------------------"
+
+ // Perform phase1 of the conversion
+ let phase1 = ConvertResumableCode env pcValInfo codeExprR
+ match phase1 with
+ | Result.Error msg ->
+ fallback msg
+ | Result.Ok phase1 ->
+
+ // Work out the initial mapping of pcs to labels
+ let pcs = [ 1 .. pcCount ]
+ let labs = pcs |> List.map (fun _ -> IL.generateCodeLabel())
+ let pc2lab = Map.ofList (List.zip pcs labs)
+
+ // Execute phase2, building the core of the method
+ if sm_verbose then printfn "----------- PHASE2 ----------------------"
+
+ // Perform phase2 to build the final expression
+ let moveNextExprR = phase1.phase2 pc2lab
+
+ if sm_verbose then printfn "----------- ADDING JUMP TABLE ----------------------"
+
+ // Add the jump table
+ let moveNextExprWithJumpTable =
+ match pcValInfo with
+ | None -> moveNextExprR
+ | Some ((v,pcValExprR),pcExprR) -> mkCompGenLet m v pcExprR (addPcJumpTable m pcs pc2lab pcValExprR moveNextExprR)
+
+ if sm_verbose then printfn "----------- REMAKE ----------------------"
+
+ // Build the result
+ let res = remake (moveNextExprWithJumpTable, phase1.stateVars, phase1.thisVars)
+ LoweredStateMachineResult.Lowered res
+
+ | _ ->
+ let msg = FSComp.SR.reprStateMachineInvalidForm()
+ fallback msg
+
+let LowerStateMachineExpr g (overallExpr: Expr) : LoweredStateMachineResult =
+ // Detect a state machine and convert it
+ let stateMachine = IsStateMachineExpr g overallExpr
+
+ match stateMachine with
+ | None -> LoweredStateMachineResult.NotAStateMachine
+ | Some altExprOpt ->
+
+ LowerStateMachine(g).Apply(overallExpr, altExprOpt)
diff --git a/src/fsharp/LowerStateMachines.fsi b/src/fsharp/LowerStateMachines.fsi
new file mode 100644
index 00000000000..5a37c4ada6f
--- /dev/null
+++ b/src/fsharp/LowerStateMachines.fsi
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+module internal FSharp.Compiler.LowerStateMachines
+
+open FSharp.Compiler.TypedTree
+open FSharp.Compiler.TcGlobals
+
+type LoweredStateMachine =
+ LoweredStateMachine of
+ templateStructTy: TType *
+ dataTy: TType *
+ stateVars: ValRef list *
+ thisVars: ValRef list *
+ moveNext: (Val * Expr) *
+ setStateMachine: (Val * Val * Expr) *
+ afterCode: (Val * Expr)
+
+type LoweredStateMachineResult =
+ /// A state machine was recognised and was compilable
+ | Lowered of LoweredStateMachine
+
+ /// A state machine was recognised and was not compilable and an alternative is available
+ | UseAlternative of message: string * alternativeExpr: Expr
+
+ /// A state machine was recognised and was not compilable and no alternative is available
+ | NoAlternative of message: string
+
+ /// The construct was not a state machine
+ | NotAStateMachine
+
+/// Analyze a TAST expression to detect the elaborated form of a state machine expression, a special kind
+/// of object expression that uses special code generation constructs.
+val LowerStateMachineExpr:
+ g: TcGlobals ->
+ overallExpr: Expr ->
+ LoweredStateMachineResult
+
diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs
index c981e3c47df..35935167200 100644
--- a/src/fsharp/Optimizer.fs
+++ b/src/fsharp/Optimizer.fs
@@ -16,6 +16,8 @@ open FSharp.Compiler.AttributeChecking
open FSharp.Compiler.CompilerGlobalState
open FSharp.Compiler.ErrorLogger
open FSharp.Compiler.Infos
+open FSharp.Compiler.Text.Range
+open FSharp.Compiler.Syntax.PrettyNaming
open FSharp.Compiler.Syntax
open FSharp.Compiler.SyntaxTreeOps
open FSharp.Compiler.TcGlobals
@@ -1252,12 +1254,6 @@ let AbstractAndRemapModulInfo msg g m (repackage, hidden) info =
// Misc helpers
//-------------------------------------------------------------------------
-/// Mark some variables (the ones we introduce via abstractBigTargets) as don't-eliminate
-let [] suffixForVariablesThatMayNotBeEliminated = "$cont"
-
-/// Indicates a ValRef generated to facilitate tuple eliminations
-let [] suffixForTupleElementAssignmentTarget = "$tupleElem"
-
/// Type applications of F# "type functions" may cause side effects, e.g.
/// let x<'a> = printfn "hello"; typeof<'a>
/// In this case do not treat them as constants.
@@ -1324,10 +1320,15 @@ let IsDiscardableEffectExpr expr =
/// Checks is a value binding is non-discardable
let ValueIsUsedOrHasEffect cenv fvs (b: Binding, binfo) =
let v = b.Var
- not (cenv.settings.EliminateUnusedBindings()) ||
+ // No discarding for debug code, except InlineIfLambda
+ (not (cenv.settings.EliminateUnusedBindings()) && not v.InlineIfLambda) ||
+ // No discarding for members
Option.isSome v.MemberInfo ||
+ // No discarding for bindings that have an effect
(binfo.HasEffect && not (IsDiscardableEffectExpr b.Expr)) ||
+ // No discarding for 'fixed'
v.IsFixed ||
+ // No discarding for things that are used
Zset.contains v (fvs())
let rec SplitValuesByIsUsedOrHasEffect cenv fvs x =
@@ -1410,14 +1411,16 @@ let TryEliminateBinding cenv _env (TBind(vspec1, e1, spBind)) e2 _m =
not vspec1.IsCompilerGenerated then
None
elif vspec1.IsFixed then None
+ elif vspec1.LogicalName.StartsWith stackVarPrefix ||
+ vspec1.LogicalName.Contains suffixForVariablesThatMayNotBeEliminated then None
else
+
// Peephole on immediate consumption of single bindings, e.g. "let x = e in x" --> "e"
// REVIEW: enhance this by general elimination of bindings to
// non-side-effecting expressions that are used only once.
// But note the cases below cover some instances of side-effecting expressions as well....
let IsUniqueUse vspec2 args =
valEq vspec1 vspec2
- && (not (vspec2.LogicalName.Contains suffixForVariablesThatMayNotBeEliminated))
// REVIEW: this looks slow. Look only for one variable instead
&& (let fvs = accFreeInExprs CollectLocals args emptyFreeVars
not (Zset.contains vspec1 fvs.FreeLocals))
@@ -1437,20 +1440,32 @@ let TryEliminateBinding cenv _env (TBind(vspec1, e1, spBind)) e2 _m =
when IsUniqueUse vspec2 [] ->
Some e1
+ // Immediate consumption of function in an application in a sequential, e.g. 'let part1 = e in part1 arg; rest'
+ // See https://github.com/fsharp/fslang-design/blob/master/tooling/FST-1034-lambda-optimizations.md
+ | Expr.Sequential(Expr.App(Expr.Val (VRefLocal vspec2, _, _), f0ty, c, args, d), rest, NormalSeq, sp, m)
+ when IsUniqueUse vspec2 (rest :: args) ->
+ Some (Expr.Sequential(Expr.App(e1, f0ty, c, args, d), rest, NormalSeq, sp, m))
+
+ // Immediate consumption of delegate via an application in a sequential, e.g. 'let part1 = e in part1.Invoke(args); rest'
+ // See https://github.com/fsharp/fslang-design/blob/master/tooling/FST-1034-lambda-optimizations.md
+ | Expr.Sequential(DelegateInvokeExpr cenv.g (invokeRef, f0ty, tyargs, Expr.Val (VRefLocal vspec2, _, _), args, _), rest, NormalSeq, sp, m)
+ when IsUniqueUse vspec2 (rest :: args) ->
+ let invoke = MakeFSharpDelegateInvokeAndTryBetaReduce cenv.g (invokeRef, e1, f0ty, tyargs, args, m)
+ Some (Expr.Sequential(invoke, rest, NormalSeq, sp, m))
+
// Immediate consumption of value by a pattern match 'let x = e in match x with ...'
| Expr.Match (spMatch, _exprm, TDSwitch(Expr.Val (VRefLocal vspec2, _, _), cases, dflt, _), targets, m, ty2)
- when (valEq vspec1 vspec2 &&
+ when (valEq vspec1 vspec2 &&
let fvs = accFreeInTargets CollectLocals targets (accFreeInSwitchCases CollectLocals cases dflt emptyFreeVars)
not (Zset.contains vspec1 fvs.FreeLocals)) ->
let spMatch = spBind.Combine spMatch
Some (Expr.Match (spMatch, e1.Range, TDSwitch(e1, cases, dflt, m), targets, m, ty2))
- // Immediate consumption of value as a function 'let f = e in f ...' and 'let x = e in f ... x ...'
+ // Immediate use of value as part of an application. 'let f = e in f ...' and 'let x = e in f ... x ...'
// Note functions are evaluated before args
// Note: do not include functions with a single arg of unit type, introduced by abstractBigTargets
- | Expr.App (f, f0ty, tyargs, args, m)
- when not (vspec1.LogicalName.Contains suffixForVariablesThatMayNotBeEliminated) ->
+ | Expr.App (f, f0ty, tyargs, args, m) ->
match GetImmediateUseContext [] (f :: args) with
| Some([], rargs) -> Some (MakeApplicationAndBetaReduce cenv.g (e1, f0ty, [tyargs], rargs, m))
| Some(f :: largs, rargs) -> Some (MakeApplicationAndBetaReduce cenv.g (f, f0ty, [tyargs], largs @ (e1 :: rargs), m))
@@ -1502,7 +1517,7 @@ let (|TDBoolSwitch|_|) dtree =
/// Check target that have a constant bool value
let (|ConstantBoolTarget|_|) target =
match target with
- | TTarget([], Expr.Const (Const.Bool b,_,_),_) -> Some b
+ | TTarget([], Expr.Const (Const.Bool b, _, _), _, _) -> Some b
| _ -> None
/// Is this a tree, where each decision is a two-way switch (to prevent later duplication of trees), and each branch returns or true/false,
@@ -1516,7 +1531,7 @@ let rec CountBoolLogicTree ((targets: DecisionTreeTarget[], costOuterCaseTree, c
| TDSuccess([], idx) ->
match targets.[idx] with
| ConstantBoolTarget result -> (if result = testBool then costOuterCaseTree else costOuterDefaultTree), 0
- | TTarget([], _exp, _) -> costOuterCaseTree + costOuterDefaultTree, 10
+ | TTarget([], _exp, _, _) -> costOuterCaseTree + costOuterDefaultTree, 10
| _ -> 100, 100
| _ -> 100, 100
@@ -1532,7 +1547,7 @@ let rec RewriteBoolLogicTree ((targets: DecisionTreeTarget[], outerCaseTree, out
| TDSuccess([], idx) ->
match targets.[idx] with
| ConstantBoolTarget result -> if result = testBool then outerCaseTree else outerDefaultTree
- | TTarget([], exp, _) -> mkBoolSwitch exp.Range exp (if testBool then outerCaseTree else outerDefaultTree) (if testBool then outerDefaultTree else outerCaseTree)
+ | TTarget([], exp, _, _) -> mkBoolSwitch exp.Range exp (if testBool then outerCaseTree else outerDefaultTree) (if testBool then outerDefaultTree else outerCaseTree)
| _ -> failwith "CountBoolLogicTree should exclude this case"
| _ -> failwith "CountBoolLogicTree should exclude this case"
@@ -1671,9 +1686,9 @@ let TryRewriteBranchingTupleBinding g (v: Val) rhs tgtSeqPtOpt body m =
match expr with
| Expr.Match (sp, inputRange, decision, targets, fullRange, ty) ->
// Recurse down every if/match branch
- let rewrittenTargets = targets |> Array.choose (fun (TTarget (vals, targetExpr, sp)) ->
+ let rewrittenTargets = targets |> Array.choose (fun (TTarget (vals, targetExpr, sp, flags)) ->
match dive g m requisites targetExpr with
- | Some rewritten -> TTarget (vals, rewritten, sp) |> Some
+ | Some rewritten -> TTarget (vals, rewritten, sp, flags) |> Some
| _ -> None)
// If not all branches can be rewritten, keep the original expression as it is
@@ -1685,7 +1700,7 @@ let TryRewriteBranchingTupleBinding g (v: Val) rhs tgtSeqPtOpt body m =
// Replace tuple allocation with mutations of locals
let _, _, _, vrefs = requisites.Value
List.map2 (mkValSet m) vrefs tupleElements
- |> mkSequentials DebugPointAtSequential.StmtOnly g m
+ |> mkSequentials DebugPointAtSequential.SuppressStmt g m
|> Some
| Expr.Sequential (e1, e2, kind, sp, m) ->
match dive g m requisites e2 with
@@ -1849,10 +1864,10 @@ let rec tryRewriteToSeqCombinators g (e: Expr) =
// match --> match
| Expr.Match (spBind, exprm, pt, targets, m, _ty) ->
- let targets = targets |> Array.map (fun (TTarget(vs, e, spTarget)) -> match tryRewriteToSeqCombinators g e with None -> None | Some e -> Some(TTarget(vs, e, spTarget)))
+ let targets = targets |> Array.map (fun (TTarget(vs, e, spTarget, flags)) -> match tryRewriteToSeqCombinators g e with None -> None | Some e -> Some(TTarget(vs, e, spTarget, flags)))
if targets |> Array.forall Option.isSome then
let targets = targets |> Array.map Option.get
- let ty = targets |> Array.pick (fun (TTarget(_, e, _)) -> Some(tyOfExpr g e))
+ let ty = targets |> Array.pick (fun (TTarget(_, e, _, _)) -> Some(tyOfExpr g e))
Some (Expr.Match (spBind, exprm, pt, targets, m, ty))
else
None
@@ -1992,12 +2007,21 @@ let rec OptimizeExpr cenv (env: IncrementalOptimizationEnv) expr =
Info=UnknownValue }
| Expr.Obj (_, ty, basev, createExpr, overrides, iimpls, m) ->
- OptimizeObjectExpr cenv env (ty, basev, createExpr, overrides, iimpls, m)
+ match expr with
+ | NewDelegateExpr cenv.g (lambdaId, vsl, body, _, remake) ->
+ OptimizeNewDelegateExpr cenv env (lambdaId, vsl, body, remake)
+ | _ ->
+ OptimizeObjectExpr cenv env (ty, basev, createExpr, overrides, iimpls, m)
| Expr.Op (op, tyargs, args, m) ->
OptimizeExprOp cenv env (op, tyargs, args, m)
| Expr.App (f, fty, tyargs, argsl, m) ->
+ match expr with
+ | DelegateInvokeExpr cenv.g (iref, fty, tyargs, delegatef, args, m) ->
+ OptimizeFSharpDelegateInvoke cenv env (iref, delegatef, fty, tyargs, args, m)
+ | _ ->
+
// eliminate uses of query
match TryDetectQueryQuoteAndRun cenv expr with
| Some newExpr -> OptimizeExpr cenv env newExpr
@@ -2049,7 +2073,7 @@ and OptimizeObjectExpr cenv env (ty, baseValOpt, basecall, overrides, iimpls, m)
let basecallR, basecallinfo = OptimizeExpr cenv env basecall
let overridesR, overrideinfos = OptimizeMethods cenv env baseValOpt overrides
let iimplsR, iimplsinfos = OptimizeInterfaceImpls cenv env baseValOpt iimpls
- let exprR=mkObjExpr(ty, baseValOpt, basecallR, overridesR, iimplsR, m)
+ let exprR = mkObjExpr (ty, baseValOpt, basecallR, overridesR, iimplsR, m)
exprR, { TotalSize=closureTotalSize + basecallinfo.TotalSize + AddTotalSizes overrideinfos + AddTotalSizes iimplsinfos
FunctionSize=1 (* a newobj *)
HasEffect=true
@@ -2222,7 +2246,7 @@ and OptimizeExprOpReductionsAfter cenv env (op, tyargs, argsR, arginfos, m) =
| _ -> None
match knownValue with
| Some valu ->
- match TryOptimizeVal cenv env (None, false, valu, m) with
+ match TryOptimizeVal cenv env (None, false, false, valu, m) with
| Some res -> OptimizeExpr cenv env res (* discard e1 since guard ensures it has no effects *)
| None -> OptimizeExprOpFallback cenv env (op, tyargs, argsR, m) arginfos valu
| None -> OptimizeExprOpFallback cenv env (op, tyargs, argsR, m) arginfos UnknownValue
@@ -2425,6 +2449,24 @@ and OptimizeLinearExpr cenv env expr contf =
let expr = if cenv.settings.ExpandStructuralValues() then ExpandStructuralBinding cenv expr else expr
let expr = stripExpr expr
+ // Matching on 'match __resumableEntry() with ...` is really a first-class language construct which we
+ // don't optimize separately
+ match expr with
+ | ResumableEntryMatchExpr cenv.g (noneBranchExpr, someVar, someBranchExpr, rebuild) ->
+ let noneBranchExprR, e1info = OptimizeExpr cenv env noneBranchExpr
+ let env = BindInternalValToUnknown cenv someVar env
+ let someBranchExprR, e2info = OptimizeExpr cenv env someBranchExpr
+ let exprR = rebuild (noneBranchExprR, someBranchExprR)
+ let infoR =
+ { TotalSize = e1info.TotalSize + e2info.TotalSize
+ FunctionSize = e1info.FunctionSize + e2info.FunctionSize
+ HasEffect = true
+ MightMakeCriticalTailcall = false
+ Info = UnknownValue }
+ contf (exprR, infoR)
+
+ | _ ->
+
match expr with
| Expr.Sequential (e1, e2, flag, spSeq, m) ->
let e1R, e1info = OptimizeExpr cenv env e1
@@ -2478,7 +2520,7 @@ and OptimizeLinearExpr cenv env expr contf =
// This ConsiderSplitToMethod is performed because it is present in OptimizeDecisionTreeTarget
let e2, e2info = ConsiderSplitToMethod cenv.settings.abstractBigTargets cenv.settings.bigTargetSize cenv env (e2, e2info)
let tinfos = [tg1info; e2info]
- let targetsR = [tg1; TTarget([], e2, spTarget2)]
+ let targetsR = [tg1; TTarget([], e2, spTarget2, None)]
OptimizeMatchPart2 cenv (spMatch, exprm, dtreeR, targetsR, dinfo, tinfos, m, ty)))
| LinearOpExpr (op, tyargs, argsHead, argLast, m) ->
@@ -2503,9 +2545,9 @@ and OptimizeTryFinally cenv env (spTry, spFinally, e1, e2, m, ty) =
if cenv.settings.EliminateTryWithAndTryFinally () && not e1info.HasEffect then
let sp =
match spTry with
- | DebugPointAtTry.Yes _ -> DebugPointAtSequential.Both
- | DebugPointAtTry.Body -> DebugPointAtSequential.Both
- | DebugPointAtTry.No -> DebugPointAtSequential.StmtOnly
+ | DebugPointAtTry.Yes _ -> DebugPointAtSequential.SuppressNeither
+ | DebugPointAtTry.Body -> DebugPointAtSequential.SuppressNeither
+ | DebugPointAtTry.No -> DebugPointAtSequential.SuppressStmt
Expr.Sequential (e1R, e2R, ThenDoSeq, sp, m), info
else
mkTryFinally cenv.g (e1R, e2R, m, ty, spTry, spFinally),
@@ -2557,16 +2599,21 @@ and OptimizeTraitCall cenv env (traitInfo, args, m) =
let argsR, arginfos = OptimizeExprsThenConsiderSplits cenv env args
OptimizeExprOpFallback cenv env (TOp.TraitCall traitInfo, [], argsR, m) arginfos UnknownValue
-and CopyExprForInlining cenv expr m =
- // Debug points are erased when doing cross-assembly inlining
- // Locals are marked compiler generated when doing cross-assembly inlining
- expr
- |> copyExpr cenv.g CloneAllAndMarkExprValsAsCompilerGenerated
- |> remarkExpr m
+and CopyExprForInlining cenv isInlineIfLambda expr m =
+ // 'InlineIfLambda' doesn't erase ranges, e.g. if the lambda is user code.
+ if isInlineIfLambda then
+ expr
+ |> copyExpr cenv.g CloneAll
+ else
+ // Debug points are erased when doing inlining
+ // Locals are marked compiler generated when doing inlining
+ expr
+ |> copyExpr cenv.g CloneAllAndMarkExprValsAsCompilerGenerated
+ |> remarkExpr m
/// Make optimization decisions once we know the optimization information
/// for a value
-and TryOptimizeVal cenv env (vOpt: ValRef option, mustInline, valInfoForVal, m) =
+and TryOptimizeVal cenv env (vOpt: ValRef option, mustInline, inlineIfLambda, valInfoForVal, m) =
match valInfoForVal with
// Inline all constants immediately
@@ -2574,13 +2621,13 @@ and TryOptimizeVal cenv env (vOpt: ValRef option, mustInline, valInfoForVal, m)
Some (Expr.Const (c, m, ty))
| SizeValue (_, detail) ->
- TryOptimizeVal cenv env (vOpt, mustInline, detail, m)
+ TryOptimizeVal cenv env (vOpt, mustInline, inlineIfLambda, detail, m)
| ValValue (vR, detail) ->
// Inline values bound to other values immediately
// Prefer to inline using the more specific info if possible
// If the more specific info didn't reveal an inline then use the value
- match TryOptimizeVal cenv env (vOpt, mustInline, detail, m) with
+ match TryOptimizeVal cenv env (vOpt, mustInline, inlineIfLambda, detail, m) with
| Some e -> Some e
| None ->
// If we have proven 'v = compilerGeneratedValue'
@@ -2598,8 +2645,8 @@ and TryOptimizeVal cenv env (vOpt: ValRef option, mustInline, valInfoForVal, m)
| ConstExprValue(_size, expr) ->
Some (remarkExpr m (copyExpr cenv.g CloneAllAndMarkExprValsAsCompilerGenerated expr))
- | CurriedLambdaValue (_, _, _, expr, _) when mustInline ->
- let exprCopy = CopyExprForInlining cenv expr m
+ | CurriedLambdaValue (_, _, _, expr, _) when mustInline || inlineIfLambda ->
+ let exprCopy = CopyExprForInlining cenv inlineIfLambda expr m
Some exprCopy
| TupleValue _ | UnionCaseValue _ | RecdValue _ when mustInline ->
@@ -2613,7 +2660,7 @@ and TryOptimizeVal cenv env (vOpt: ValRef option, mustInline, valInfoForVal, m)
| _ -> None
and TryOptimizeValInfo cenv env m vinfo =
- if vinfo.HasEffect then None else TryOptimizeVal cenv env (None, false, vinfo.Info, m)
+ if vinfo.HasEffect then None else TryOptimizeVal cenv env (None, false, false, vinfo.Info, m)
/// Add 'v1 = v2' information into the information stored about a value
and AddValEqualityInfo g m (v: ValRef) info =
@@ -2632,7 +2679,7 @@ and AddValEqualityInfo g m (v: ValRef) info =
and OptimizeVal cenv env expr (v: ValRef, m) =
let valInfoForVal = GetInfoForVal cenv env m v
- match TryOptimizeVal cenv env (Some v, v.MustInline, valInfoForVal.ValExprInfo, m) with
+ match TryOptimizeVal cenv env (Some v, v.MustInline, v.InlineIfLambda, valInfoForVal.ValExprInfo, m) with
| Some e ->
// don't reoptimize inlined lambdas until they get applied to something
match e with
@@ -2649,7 +2696,10 @@ and OptimizeVal cenv env expr (v: ValRef, m) =
e, AddValEqualityInfo cenv.g m v einfo
| None ->
- if v.MustInline then error(Error(FSComp.SR.optFailedToInlineValue(v.DisplayName), m))
+ if v.MustInline then
+ error(Error(FSComp.SR.optFailedToInlineValue(v.DisplayName), m))
+ if v.InlineIfLambda then
+ warning(Error(FSComp.SR.optFailedToInlineSuggestedValue(v.DisplayName), m))
expr, (AddValEqualityInfo cenv.g m v
{ Info=valInfoForVal.ValExprInfo
HasEffect=false
@@ -2957,7 +3007,7 @@ and TryInlineApplication cenv env finfo (tyargs: TType list, args: Expr list, m)
// Inlining lambda
(* ---------- printf "Inlining lambda near %a = %s\n" outputRange m (showL (exprL f2)) (* JAMES: *) ----------*)
- let f2R = CopyExprForInlining cenv f2 m
+ let f2R = CopyExprForInlining cenv false f2 m
// Optimizing arguments after inlining
@@ -2972,6 +3022,88 @@ and TryInlineApplication cenv env finfo (tyargs: TType list, args: Expr list, m)
| _ -> None
+// Optimize the application of computed functions.
+// See https://github.com/fsharp/fslang-design/blob/master/tooling/FST-1034-lambda-optimizations.md
+//
+// Always lift 'let', 'letrec', sequentials and 'match' off computed functions so
+// (let x = 1 in fexpr) arg ---> let x = 1 in fexpr arg
+// (let rec binds in fexpr) arg ---> let rec binds in fexpr arg
+// (e; fexpr) arg ---> e; fexpr arg
+// (match e with pat1 -> func1 | pat2 -> func2) args --> (match e with pat1 -> func1 args | pat2 -> func2 args)
+//
+// This is always valid because functions are computed before arguments.
+// We do this even in debug code as it doesn't change debugging properties.
+// This is useful in DSLs that compute functions and weave them together with user code, e.g.
+// inline F# computation expressions.
+//
+// The case of 'match' is particularly awkward because we are cloning 'args' on the right. We want to avoid
+// this in the common case, so we first collect up all the "function holes"
+// (let x = 1 in )
+// (let rec binds in )
+// (e; )
+// (match e with pat1 -> | pat2 -> )
+// then work out if we only have one of them. While collecting up the holes we build up a function to rebuild the
+// overall expression given new expressions ("func" --> "func args" and its optimization).
+//
+// If there a multiple holes, we had a "match" somewhere, and we abandon OptimizeApplication and simply apply the
+// function to the arguments at each hole (copying the arguments), then reoptimize the whole result.
+//
+// If there is a single hole, we proceed with OptimizeApplication
+and StripPreComputationsFromComputedFunction g f0 args mkApp =
+
+ // Identify sub-expressions that are the lambda functions to apply.
+ // There may be more than one because of multiple 'match' branches.
+ let rec strip (f: Expr) : Expr list * (Expr list -> Expr) =
+ match stripExpr f with
+ | Expr.Let (bind, bodyExpr, m, _) ->
+ let fs, remake = strip bodyExpr
+ fs, (remake >> mkLetBind m bind)
+ | Expr.LetRec (binds, bodyExpr, m, _) ->
+ let fs, remake = strip bodyExpr
+ fs, (remake >> mkLetRecBinds m binds)
+ | Expr.Sequential (x1, bodyExpr, NormalSeq, sp, m) ->
+ let fs, remake = strip bodyExpr
+ fs, (remake >> (fun bodyExpr2 -> Expr.Sequential (x1, bodyExpr2, NormalSeq, sp, m)))
+
+ // Matches which compute a different function on each branch are awkward, see above.
+ | Expr.Match (spMatch, exprm, dtree, targets, dflt, _ty) when targets.Length <= 2 ->
+ let fsl, targetRemakes =
+ targets
+ |> Array.map (fun (TTarget(vs, bodyExpr, spTarget, flags)) ->
+ let fs, remake = strip bodyExpr
+ fs, (fun holes -> TTarget(vs, remake holes, spTarget, flags)))
+ |> Array.unzip
+
+ let fs = List.concat fsl
+ let chunkSizes = Array.map List.length fsl
+ let remake (newExprs: Expr list) =
+ let newExprsInChunks, _ =
+ ((newExprs,0), chunkSizes) ||> Array.mapFold (fun (acc,i) chunkSize ->
+ let chunk = acc.[0..chunkSize-1]
+ let acc = acc.[chunkSize..]
+ chunk, (acc, i+chunkSize))
+ let targetsR = (newExprsInChunks, targetRemakes) ||> Array.map2 (fun newExprsChunk targetRemake -> targetRemake newExprsChunk)
+ let tyR = tyOfExpr g targetsR.[0].TargetExpression
+ Expr.Match (spMatch, exprm, dtree, targetsR, dflt, tyR)
+ fs, remake
+
+ | _ ->
+ [f], (fun newExprs -> (assert (newExprs.Length = 1)); List.head newExprs)
+
+ match strip f0 with
+ | [f], remake ->
+ // If the computed function has only one interesting function result expression then progress as normal
+ Choice2Of2 (f, (fun x -> remake [x]))
+ | fs, remake ->
+ // If there is a match with multiple branches then apply each function to a copy of the arguments,
+ // remake the whole expression and return an indicator to reoptimize that.
+ let applied =
+ fs |> List.mapi (fun i f ->
+ let argsR = if i = 0 then args else List.map (copyExpr g CloneAll) args
+ mkApp f argsR)
+ let remade = remake applied
+ Choice1Of2 remade
+
/// When optimizing a function in an application, use the whole range including arguments for the range
/// to apply to 'inline' code
and OptimizeFuncInApplication cenv env f0 mWithArgs =
@@ -2984,17 +3116,24 @@ and OptimizeFuncInApplication cenv env f0 mWithArgs =
/// Optimize/analyze an application of a function to type and term arguments
and OptimizeApplication cenv env (f0, f0ty, tyargs, args, m) =
+ let g = cenv.g
// trying to devirtualize
match TryDevirtualizeApplication cenv env (f0, tyargs, args, m) with
| Some res ->
// devirtualized
res
| None ->
- let newf0, finfo = OptimizeFuncInApplication cenv env f0 m
+ let optf0, finfo = OptimizeFuncInApplication cenv env f0 m
+
+ match StripPreComputationsFromComputedFunction g optf0 args (fun f argsR -> MakeApplicationAndBetaReduce g (f, tyOfExpr g f, [tyargs], argsR, f.Range)) with
+ | Choice1Of2 remade ->
+ OptimizeExpr cenv env remade
+ | Choice2Of2 (newf0, remake) ->
+
match TryInlineApplication cenv env finfo (tyargs, args, m) with
- | Some res ->
+ | Some (res, info) ->
// inlined
- res
+ (res |> remake), info
| None ->
let shapes =
@@ -3017,9 +3156,10 @@ and OptimizeApplication cenv env (f0, f0ty, tyargs, args, m) =
let newArgs, arginfos = OptimizeExprsThenReshapeAndConsiderSplits cenv env shapes
// beta reducing
- let newExpr = MakeApplicationAndBetaReduce cenv.g (newf0, f0ty, [tyargs], newArgs, m)
+ let reducedExpr = MakeApplicationAndBetaReduce g (newf0, f0ty, [tyargs], newArgs, m)
+ let newExpr = reducedExpr |> remake
- match newf0, newExpr with
+ match newf0, reducedExpr with
| (Expr.Lambda _ | Expr.TyLambda _), Expr.Let _ ->
// we beta-reduced, hence reoptimize
OptimizeExpr cenv env newExpr
@@ -3058,6 +3198,31 @@ and OptimizeApplication cenv env (f0, f0ty, tyargs, args, m) =
HasEffect=true
MightMakeCriticalTailcall = mayBeCriticalTailcall
Info=ValueOfExpr newExpr }
+
+and OptimizeFSharpDelegateInvoke cenv env (invokeRef, f0, f0ty, tyargs, args, m) =
+ let g = cenv.g
+ let optf0, finfo = OptimizeExpr cenv env f0
+
+ match StripPreComputationsFromComputedFunction g optf0 args (fun f argsR -> MakeFSharpDelegateInvokeAndTryBetaReduce g (invokeRef, f, f0ty, tyargs, argsR, m)) with
+ | Choice1Of2 remade ->
+ OptimizeExpr cenv env remade
+ | Choice2Of2 (newf0, remake) ->
+
+ let newArgs, arginfos = OptimizeExprsThenConsiderSplits cenv env args
+ let reducedExpr = MakeFSharpDelegateInvokeAndTryBetaReduce g (invokeRef, newf0, f0ty, tyargs, newArgs, m)
+ let newExpr = reducedExpr |> remake
+ match newf0, reducedExpr with
+ | Expr.Obj _, Expr.Let _ ->
+ // we beta-reduced, hence reoptimize
+ OptimizeExpr cenv env newExpr
+ | _ ->
+ // no reduction, return
+ newExpr, { TotalSize=finfo.TotalSize + AddTotalSizes arginfos
+ FunctionSize=finfo.FunctionSize + AddFunctionSizes arginfos
+ HasEffect=true
+ MightMakeCriticalTailcall = true
+ Info=ValueOfExpr newExpr }
+
/// Optimize/analyze a lambda expression
and OptimizeLambdas (vspec: Val option) cenv env topValInfo e ety =
@@ -3125,8 +3290,23 @@ and OptimizeLambdas (vspec: Val option) cenv env topValInfo e ety =
HasEffect=false
MightMakeCriticalTailcall = false
Info= valu }
+
| _ -> OptimizeExpr cenv env e
+and OptimizeNewDelegateExpr cenv env (lambdaId, vsl, body, remake) =
+ let env = List.foldBack (BindInternalValsToUnknown cenv) vsl env
+ let bodyR, bodyinfo = OptimizeExpr cenv env body
+ let arities = vsl.Length
+ let bsize = bodyinfo.TotalSize
+ let exprR = remake bodyR
+ let valu = CurriedLambdaValue (lambdaId, arities, bsize, exprR, tyOfExpr cenv.g exprR)
+
+ exprR, { TotalSize=bsize + closureTotalSize (* estimate size of new syntactic closure - expensive, in contrast to a method *)
+ FunctionSize=1
+ HasEffect=false
+ MightMakeCriticalTailcall = false
+ Info= valu }
+
/// Recursive calls that first try to make an expression "fit" the a shape
/// where it is about to be consumed.
and OptimizeExprsThenReshapeAndConsiderSplits cenv env exprs =
@@ -3229,12 +3409,12 @@ and RebuildOptimizedMatch (spMatch, exprm, m, ty, dtree, tgs, dinfo, tinfos) =
expr, einfo
/// Optimize/analyze a target of a decision tree
-and OptimizeDecisionTreeTarget cenv env _m (TTarget(vs, expr, spTarget)) =
+and OptimizeDecisionTreeTarget cenv env _m (TTarget(vs, expr, spTarget, flags)) =
let env = BindInternalValsToUnknown cenv vs env
let exprR, einfo = OptimizeExpr cenv env expr
let exprR, einfo = ConsiderSplitToMethod cenv.settings.abstractBigTargets cenv.settings.bigTargetSize cenv env (exprR, einfo)
let evalueR = AbstractExprInfoByVars (vs, []) einfo.Info
- TTarget(vs, exprR, spTarget),
+ TTarget(vs, exprR, spTarget, flags),
{ TotalSize=einfo.TotalSize
FunctionSize=einfo.FunctionSize
HasEffect=einfo.HasEffect
@@ -3333,7 +3513,7 @@ and OptimizeBinding cenv isRec env (TBind(vref, expr, spBind)) =
let exprOptimized, einfo =
let env = if vref.IsCompilerGenerated && Option.isSome env.latestBoundId then env else {env with latestBoundId=Some vref.Id}
- let cenv = if vref.InlineInfo = ValInline.PseudoVal then { cenv with optimizing=false} else cenv
+ let cenv = if vref.InlineInfo.MustInline then { cenv with optimizing=false} else cenv
let arityInfo = InferArityOfExprBinding cenv.g AllowTypeDirectedDetupling.No vref expr
let exprOptimized, einfo = OptimizeLambdas (Some vref) cenv env arityInfo expr vref.Type
let size = localVarSize
@@ -3362,10 +3542,10 @@ and OptimizeBinding cenv isRec env (TBind(vref, expr, spBind)) =
| UnknownValue | ConstValue _ | ConstExprValue _ -> ivalue
| SizeValue(_, a) -> MakeSizedValueInfo (cut a)
- let einfo = if vref.MustInline then einfo else {einfo with Info = cut einfo.Info }
+ let einfo = if vref.MustInline || vref.InlineIfLambda then einfo else {einfo with Info = cut einfo.Info }
let einfo =
- if (not vref.MustInline && not (cenv.settings.KeepOptimizationValues())) ||
+ if (not vref.MustInline && not vref.InlineIfLambda && not (cenv.settings.KeepOptimizationValues())) ||
// Bug 4916: do not record inline data for initialization trigger expressions
// Note: we can't eliminate these value infos at the file boundaries because that would change initialization
diff --git a/src/fsharp/PatternMatchCompilation.fs b/src/fsharp/PatternMatchCompilation.fs
index 403995686d9..8c717e790a4 100644
--- a/src/fsharp/PatternMatchCompilation.fs
+++ b/src/fsharp/PatternMatchCompilation.fs
@@ -80,7 +80,7 @@ and TypedMatchClause =
member c.Pattern = let (TClause(p, _, _, _)) = c in p
member c.Range = let (TClause(_, _, _, m)) = c in m
member c.Target = let (TClause(_, _, tg, _)) = c in tg
- member c.BoundVals = let (TClause(_p, _whenOpt, TTarget(vs, _, _), _m)) = c in vs
+ member c.BoundVals = let (TClause(_p, _whenOpt, TTarget(vs, _, _, _), _m)) = c in vs
let debug = false
@@ -854,7 +854,7 @@ let CompilePatternBasic
// Note we don't emit sequence points at either the succeeding or failing targets of filters since if
// the exception is filtered successfully then we will run the handler and hit the sequence point there.
// That sequence point will have the pattern variables bound, which is exactly what we want.
- let tg = TTarget(List.empty, throwExpr, DebugPointForTarget.No)
+ let tg = TTarget([], throwExpr, DebugPointForTarget.No, None)
let _ = matchBuilder.AddTarget tg
let clause = TClause(TPat_wild matchm, None, tg, matchm)
incompleteMatchClauseOnce <- Some clause
@@ -871,7 +871,9 @@ let CompilePatternBasic
clausesA.[i]
elif i = nClauses then getIncompleteMatchClause refuted
else failwith "GetClause"
+
let GetValsBoundByClause i refuted = (GetClause i refuted).BoundVals
+
let GetWhenGuardOfClause i refuted = (GetClause i refuted).GuardExpr
// Different uses of parameterized active patterns have different identities as far as paths are concerned.
@@ -1424,7 +1426,7 @@ let rec CompilePattern g denv amap tcVal infoReader exprm matchm warnOnUnused a
else DebugPointForTarget.Yes
// Make the clause that represents the remaining cases of the pattern match
- let clauseForRestOfMatch = TClause(TPat_wild matchm, None, TTarget(List.empty, expr, spTarget), matchm)
+ let clauseForRestOfMatch = TClause(TPat_wild matchm, None, TTarget(List.empty, expr, spTarget, None), matchm)
CompilePatternBasic g denv amap tcVal infoReader exprm matchm warnOnUnused warnOnIncomplete actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) (group @ [clauseForRestOfMatch]) inputTy resultTy
diff --git a/src/fsharp/PostInferenceChecks.fs b/src/fsharp/PostInferenceChecks.fs
index 6008120291e..64c200f4d9e 100644
--- a/src/fsharp/PostInferenceChecks.fs
+++ b/src/fsharp/PostInferenceChecks.fs
@@ -66,6 +66,15 @@ open FSharp.Compiler.TypeRelations
// check environment
//--------------------------------------------------------------------------
+[]
+type Resumable =
+ | None
+ /// Indicates we are expecting resumable code (the body of a ResumableCode delegate or
+ /// the body of the MoveNextMethod for a state machine)
+ /// -- allowed: are we inside the 'then' branch of an 'if __useResumableCode then ...'
+ /// for a ResumableCode delegate.
+ | ResumableExpr of allowed: bool
+
type env =
{
/// The bound type parameter names in scope
@@ -97,6 +106,9 @@ type env =
/// Are we in an app expression (Expr.App)?
isInAppExpr: bool
+
+ /// Are we expecting a resumable code block etc
+ resumableCode: Resumable
}
override _.ToString() = ""
@@ -980,6 +992,99 @@ and CheckExprLinear (cenv: cenv) (env: env) expr (context: PermitByRefExpr) (con
// not a linear expression
contf (CheckExpr cenv env expr context)
+/// Check a resumable code expression (the body of a ResumableCode delegate or
+/// the body of the MoveNextMethod for a state machine)
+and TryCheckResumableCodeConstructs cenv env expr : bool =
+ let g = cenv.g
+
+ match env.resumableCode with
+ | Resumable.None ->
+ CheckNoResumableStmtConstructs cenv env expr
+ false
+ | Resumable.ResumableExpr allowed ->
+ match expr with
+ | IfUseResumableStateMachinesExpr g (thenExpr, elseExpr) ->
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.ResumableExpr true } thenExpr
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } elseExpr
+ true
+
+ | ResumableEntryMatchExpr g (noneBranchExpr, someVar, someBranchExpr, _rebuild) ->
+ if not allowed then
+ errorR(Error(FSComp.SR.tcInvalidResumableConstruct("__resumableEntry"), expr.Range))
+ CheckExprNoByrefs cenv env noneBranchExpr
+ BindVal cenv env someVar
+ CheckExprNoByrefs cenv env someBranchExpr
+ true
+
+ | ResumeAtExpr g pcExpr ->
+ if not allowed then
+ errorR(Error(FSComp.SR.tcInvalidResumableConstruct("__resumeAt"), expr.Range))
+ CheckExprNoByrefs cenv env pcExpr
+ true
+
+ | ResumableCodeInvoke g (_, f, args, _, _) ->
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } f
+ for arg in args do
+ CheckExprPermitByRefLike cenv { env with resumableCode = Resumable.None } arg |> ignore
+ true
+
+ | SequentialResumableCode g (e1, e2, _m, _recreate) ->
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.ResumableExpr allowed }e1
+ CheckExprNoByrefs cenv env e2
+ true
+
+ | WhileExpr (_sp1, _sp2, guardExpr, bodyExpr, _m) ->
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } guardExpr
+ CheckExprNoByrefs cenv env bodyExpr
+ true
+
+ // Integer for-loops are allowed but their bodies are not currently resumable
+ | ForLoopExpr (_sp1, _sp2, e1, e2, v, e3, _m) ->
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } e1
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } e2
+ BindVal cenv env v
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } e3
+ true
+
+ | TryWithExpr (_spTry, _spWith, _resTy, bodyExpr, _filterVar, filterExpr, _handlerVar, handlerExpr, _m) ->
+ CheckExprNoByrefs cenv env bodyExpr
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } handlerExpr
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } filterExpr
+ true
+
+ | TryFinallyExpr (_sp1, _sp2, _ty, e1, e2, _m) ->
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } e1
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.None } e2
+ true
+
+ | Expr.Match (_spBind, _exprm, dtree, targets, _m, _ty) ->
+ targets |> Array.iter(fun (TTarget(vs, targetExpr, _spTarget, _)) ->
+ BindVals cenv env vs
+ CheckExprNoByrefs cenv env targetExpr)
+ CheckDecisionTree cenv { env with resumableCode = Resumable.None } dtree
+ true
+
+ | Expr.Let (bind, bodyExpr, _m, _)
+ // Restriction: resumable code can't contain local constrained generic functions
+ when bind.Var.IsCompiledAsTopLevel || not (IsGenericValWithGenericConstraints g bind.Var) ->
+ CheckBinding cenv { env with resumableCode = Resumable.None } false PermitByRefExpr.Yes bind |> ignore
+ BindVal cenv env bind.Var
+ CheckExprNoByrefs cenv env bodyExpr
+ true
+
+ // LetRec bindings may not appear as part of resumable code (more careful work is needed to make them compilable)
+ | Expr.LetRec(_bindings, bodyExpr, _range, _frees) when allowed ->
+ errorR(Error(FSComp.SR.tcResumableCodeContainsLetRec(), expr.Range))
+ CheckExprNoByrefs cenv env bodyExpr
+ true
+
+ // This construct arises from the 'mkDefault' in the 'Throw' case of an incomplete pattern match
+ | Expr.Const (Const.Zero, _, _) ->
+ true
+
+ | _ ->
+ false
+
/// Check an expression, given information about the position of the expression
and CheckExpr (cenv: cenv) (env: env) origExpr (context: PermitByRefExpr) : Limit =
let g = cenv.g
@@ -991,6 +1096,15 @@ and CheckExpr (cenv: cenv) (env: env) origExpr (context: PermitByRefExpr) : Limi
let expr = NormalizeAndAdjustPossibleSubsumptionExprs g origExpr
let expr = stripExpr expr
+ match TryCheckResumableCodeConstructs cenv env expr with
+ | true ->
+ // we've handled the special cases of resumable code and don't do other checks.
+ NoLimit
+ | false ->
+
+ // Handle ResumableExpr --> other expression
+ let env = { env with resumableCode = Resumable.None }
+
match expr with
| LinearOpExpr _
| LinearMatchExpr _
@@ -1034,9 +1148,22 @@ and CheckExpr (cenv: cenv) (env: env) origExpr (context: PermitByRefExpr) : Limi
CheckTypeNoByrefs cenv env m ty
NoLimit
+ | StructStateMachineExpr g (_dataTy,
+ (moveNextThisVar, moveNextExpr),
+ (setStateMachineThisVar, setStateMachineStateVar, setStateMachineBody),
+ (afterCodeThisVar, afterCodeBody)) ->
+ if not (g.langVersion.SupportsFeature LanguageFeature.ResumableStateMachines) then
+ error(Error(FSComp.SR.tcResumableCodeNotSupported(), expr.Range))
+
+ BindVals cenv env [moveNextThisVar; setStateMachineThisVar; setStateMachineStateVar; afterCodeThisVar]
+ CheckExprNoByrefs cenv { env with resumableCode = Resumable.ResumableExpr true } moveNextExpr
+ CheckExprNoByrefs cenv env setStateMachineBody
+ CheckExprNoByrefs cenv env afterCodeBody
+ NoLimit
+
| Expr.Obj (_, ty, basev, superInitCall, overrides, iimpls, m) ->
CheckExprNoByrefs cenv env superInitCall
- CheckMethods cenv env basev overrides
+ CheckMethods cenv env basev (ty, overrides)
CheckInterfaceImpls cenv env basev iimpls
CheckTypeNoByrefs cenv env m ty
@@ -1113,6 +1240,11 @@ and CheckExpr (cenv: cenv) (env: env) origExpr (context: PermitByRefExpr) : Limi
// Check an application
| Expr.App (f, _fty, tyargs, argsl, m) ->
+ match expr with
+ | ResumableCodeInvoke g _ ->
+ warning(Error(FSComp.SR.tcResumableCodeInvocation(), m))
+ | _ -> ()
+
let returnTy = tyOfExpr g expr
// This is to handle recursive cases. Don't check 'returnTy' again if we are still inside a app expression.
@@ -1178,13 +1310,21 @@ and CheckExpr (cenv: cenv) (env: env) origExpr (context: PermitByRefExpr) : Limi
| Expr.Link _ ->
failwith "Unexpected reclink"
-and CheckMethods cenv env baseValOpt methods =
- methods |> List.iter (CheckMethod cenv env baseValOpt)
+and CheckMethods cenv env baseValOpt (ty, methods) =
+ methods |> List.iter (CheckMethod cenv env baseValOpt ty)
-and CheckMethod cenv env baseValOpt (TObjExprMethod(_, attribs, tps, vs, body, m)) =
+and CheckMethod cenv env baseValOpt ty (TObjExprMethod(_, attribs, tps, vs, body, m)) =
let env = BindTypars cenv.g env tps
let vs = List.concat vs
let env = BindArgVals env vs
+ let env =
+ // Body of ResumableCode delegate
+ if isResumableCodeTy cenv.g ty then
+ if not (cenv.g.langVersion.SupportsFeature LanguageFeature.ResumableStateMachines) then
+ error(Error(FSComp.SR.tcResumableCodeNotSupported(), m))
+ { env with resumableCode = Resumable.ResumableExpr false }
+ else
+ { env with resumableCode = Resumable.None }
CheckAttribs cenv env attribs
CheckNoReraise cenv None body
CheckEscapes cenv true m (match baseValOpt with Some x -> x :: vs | None -> vs) body |> ignore
@@ -1193,11 +1333,22 @@ and CheckMethod cenv env baseValOpt (TObjExprMethod(_, attribs, tps, vs, body, m
and CheckInterfaceImpls cenv env baseValOpt l =
l |> List.iter (CheckInterfaceImpl cenv env baseValOpt)
-and CheckInterfaceImpl cenv env baseValOpt (_ty, overrides) =
+and CheckInterfaceImpl cenv env baseValOpt overrides =
CheckMethods cenv env baseValOpt overrides
+and CheckNoResumableStmtConstructs cenv _env expr =
+ let g = cenv.g
+ match expr with
+ | Expr.Val (v, _, m)
+ when valRefEq g v g.cgh__resumeAt_vref ||
+ valRefEq g v g.cgh__resumableEntry_vref ||
+ valRefEq g v g.cgh__stateMachine_vref ->
+ errorR(Error(FSComp.SR.tcInvalidResumableConstruct(v.DisplayName), m))
+ | _ -> ()
+
and CheckExprOp cenv env (op, tyargs, args, m) context expr =
let g = cenv.g
+
let ctorLimitedZoneCheck() =
if env.ctorLimitedZone then errorR(Error(FSComp.SR.chkObjCtorsCantUseExceptionHandling(), m))
@@ -1475,18 +1626,20 @@ and CheckExprOp cenv env (op, tyargs, args, m) context expr =
CheckTypeInstNoByrefs cenv env m tyargs
CheckExprsNoByRefLike cenv env args
-and CheckLambdas isTop (memInfo: ValMemberInfo option) cenv env inlined topValInfo alwaysCheckNoReraise e mOrig ety context =
+and CheckLambdas isTop (memberVal: Val option) cenv env inlined topValInfo alwaysCheckNoReraise expr mOrig ety context =
let g = cenv.g
+ let memInfo = memberVal |> Option.bind (fun v -> v.MemberInfo)
+
// The topValInfo here says we are _guaranteeing_ to compile a function value
// as a .NET method with precisely the corresponding argument counts.
- match e with
+ match expr with
| Expr.TyChoose (tps, e1, m) ->
let env = BindTypars g env tps
- CheckLambdas isTop memInfo cenv env inlined topValInfo alwaysCheckNoReraise e1 m ety context
+ CheckLambdas isTop memberVal cenv env inlined topValInfo alwaysCheckNoReraise e1 m ety context
| Expr.Lambda (_, _, _, _, _, m, _)
| Expr.TyLambda (_, _, _, m, _) ->
- let tps, ctorThisValOpt, baseValOpt, vsl, body, bodyty = destTopLambda g cenv.amap topValInfo (e, ety) in
+ let tps, ctorThisValOpt, baseValOpt, vsl, body, bodyty = destTopLambda g cenv.amap topValInfo (expr, ety)
let env = BindTypars g env tps
let thisAndBase = Option.toList ctorThisValOpt @ Option.toList baseValOpt
let restArgs = List.concat vsl
@@ -1514,6 +1667,9 @@ and CheckLambdas isTop (memInfo: ValMemberInfo option) cenv env inlined topValIn
// Check argument types
syntacticArgs
|> List.iter (fun arg ->
+ if arg.InlineIfLambda && (not inlined || not (isFunTy g arg.Type || isFSharpDelegateTy g arg.Type)) then
+ errorR(Error(FSComp.SR.tcInlineIfLambdaUsedOnNonInlineFunctionOrMethod(), arg.Range))
+
CheckValSpecAux permitByRefType cenv env arg (fun () ->
if arg.IsCompilerGenerated then
errorR(Error(FSComp.SR.chkErrorUseOfByref(), arg.Range))
@@ -1568,13 +1724,13 @@ and CheckLambdas isTop (memInfo: ValMemberInfo option) cenv env inlined topValIn
let limit =
if not inlined && (isByrefLikeTy g m ety || isNativePtrTy g ety) then
// allow byref to occur as RHS of byref binding.
- CheckExpr cenv env e context
+ CheckExpr cenv env expr context
else
- CheckExprNoByrefs cenv env e
+ CheckExprNoByrefs cenv env expr
NoLimit
if alwaysCheckNoReraise then
- CheckNoReraise cenv None e
+ CheckNoReraise cenv None expr
limit
and CheckExprs cenv env exprs contexts : Limit =
@@ -1588,7 +1744,7 @@ and CheckExprsNoByRefLike cenv env exprs : Limit =
exprs |> List.iter (CheckExprNoByrefs cenv env)
NoLimit
-and CheckExprsPermitByRefLike cenv env exprs =
+and CheckExprsPermitByRefLike cenv env exprs : Limit =
exprs
|> List.map (CheckExprPermitByRefLike cenv env)
|> CombineLimits
@@ -1609,7 +1765,7 @@ and CheckDecisionTreeTargets cenv env targets context =
|> Array.map (CheckDecisionTreeTarget cenv env context)
|> (CombineLimits << List.ofArray)
-and CheckDecisionTreeTarget cenv env context (TTarget(vs, e, _)) =
+and CheckDecisionTreeTarget cenv env context (TTarget(vs, e, _, _)) =
BindVals cenv env vs
vs |> List.iter (CheckValSpec PermitByRefType.All cenv env)
CheckExpr cenv env e context
@@ -1838,7 +1994,23 @@ and CheckBinding cenv env alwaysCheckNoReraise context (TBind(v, bindRhs, _) as
let topValInfo = match bind.Var.ValReprInfo with Some info -> info | _ -> ValReprInfo.emptyValData
- CheckLambdas isTop v.MemberInfo cenv env v.MustInline topValInfo alwaysCheckNoReraise bindRhs v.Range v.Type context
+ // If the method has ResumableCode argument or return type it must be inline
+ // unless warning is suppressed (user must know what they're doing).
+ //
+ // If the method has ResumableCode return attribute we check the body w.r.t. that
+ let env =
+ if cenv.reportErrors && isReturnsResumableCodeTy g v.TauType then
+ if not (g.langVersion.SupportsFeature LanguageFeature.ResumableStateMachines) then
+ error(Error(FSComp.SR.tcResumableCodeNotSupported(), bind.Var.Range))
+ if not v.MustInline then
+ warning(Error(FSComp.SR.tcResumableCodeFunctionMustBeInline(), v.Range))
+
+ if isReturnsResumableCodeTy g v.TauType then
+ { env with resumableCode = Resumable.ResumableExpr false }
+ else
+ env
+
+ CheckLambdas isTop (Some v) cenv env v.MustInline topValInfo alwaysCheckNoReraise bindRhs v.Range v.Type context
and CheckBindings cenv env xs =
xs |> List.iter (CheckBinding cenv env false PermitByRefExpr.Yes >> ignore)
@@ -2355,8 +2527,8 @@ let CheckTopImpl (g, amap, reportErrors, infoReader, internalsVisibleToPaths, vi
let cenv =
{ g =g
reportErrors=reportErrors
- boundVals= new Dictionary<_, _>(100, HashIdentity.Structural)
- limitVals= new Dictionary<_, _>(100, HashIdentity.Structural)
+ boundVals = Dictionary<_, _>(100, HashIdentity.Structural)
+ limitVals = Dictionary<_, _>(100, HashIdentity.Structural)
potentialUnboundUsesOfVals=Map.empty
anonRecdTypes = StampMap.Empty
usesQuotations=false
@@ -2391,7 +2563,8 @@ let CheckTopImpl (g, amap, reportErrors, infoReader, internalsVisibleToPaths, vi
reflect=false
external=false
returnScope = 0
- isInAppExpr = false }
+ isInAppExpr = false
+ resumableCode = Resumable.None }
CheckModuleExpr cenv env mexpr
CheckAttribs cenv env extraAttribs
diff --git a/src/fsharp/PrettyNaming.fs b/src/fsharp/PrettyNaming.fs
index 8bdcd36e120..e6072ad62c5 100755
--- a/src/fsharp/PrettyNaming.fs
+++ b/src/fsharp/PrettyNaming.fs
@@ -769,3 +769,9 @@ let FSharpOptimizationDataResourceName2 = "FSharpOptimizationInfo."
let FSharpSignatureDataResourceName2 = "FSharpSignatureInfo."
+let [] suffixForVariablesThatMayNotBeEliminated = "$cont"
+
+let [] suffixForTupleElementAssignmentTarget = "$tupleElem"
+
+let [] stackVarPrefix = "__stack_"
+
diff --git a/src/fsharp/PrettyNaming.fsi b/src/fsharp/PrettyNaming.fsi
index 8c67b0ceb4f..e03cf533e53 100644
--- a/src/fsharp/PrettyNaming.fsi
+++ b/src/fsharp/PrettyNaming.fsi
@@ -180,3 +180,16 @@ val GetLongNameFromString: string -> string list
val FormatAndOtherOverloadsString: int -> string
+val FSharpSignatureDataResourceName2: string
+
+/// Mark some variables (the ones we introduce via abstractBigTargets) as don't-eliminate
+[]
+val internal suffixForVariablesThatMayNotBeEliminated : string = "$cont"
+
+/// Indicates a ValRef generated to facilitate tuple eliminations
+[]
+val internal suffixForTupleElementAssignmentTarget : string = "$tupleElem"
+
+[]
+val internal stackVarPrefix : string = "__stack_"
+
diff --git a/src/fsharp/QuotationTranslator.fs b/src/fsharp/QuotationTranslator.fs
index 166d62a0b6f..9ae96ba19b7 100644
--- a/src/fsharp/QuotationTranslator.fs
+++ b/src/fsharp/QuotationTranslator.fs
@@ -203,7 +203,7 @@ let (|ObjectInitializationCheck|_|) g expr =
(
Expr.Op (TOp.ILAsm ([AI_clt], _), _, [Expr.Op (TOp.ValFieldGet ((RecdFieldRef(_, name))), _, [Expr.Val (selfRef, NormalValUse, _)], _); Expr.Const (Const.Int32 1, _, _)], _), _, _, _
),
- [| TTarget([], Expr.App (Expr.Val (failInitRef, _, _), _, _, _, _), _); _ |], _, resultTy
+ [| TTarget([], Expr.App (Expr.Val (failInitRef, _, _), _, _, _, _), _, _); _ |], _, resultTy
) when
IsCompilerGeneratedName name &&
name.StartsWithOrdinal("init") &&
@@ -479,7 +479,6 @@ and private ConvExprCore cenv (env : QuotationTranslationEnv) (expr: Expr) : QP.
let typR = ConvType cenv env m retTy
ConvDecisionTree cenv env tgs typR dtree
- // initialization check
| Expr.Sequential (ObjectInitializationCheck g, x1, NormalSeq, _, _) ->
ConvExpr cenv env x1
@@ -816,7 +815,7 @@ and ConvLValueArgs cenv env args =
| obj :: rest -> ConvLValueExpr cenv env obj :: ConvExprs cenv env rest
| [] -> []
-and ConvLValueExpr cenv env expr =
+and ConvLValueExpr cenv env (expr: Expr) =
EmitDebugInfoIfNecessary cenv env expr.Range (ConvLValueExprCore cenv env expr)
// This function has to undo the work of mkExprAddrOfExpr
@@ -1105,7 +1104,7 @@ and ConvDecisionTree cenv env tgs typR x =
EmitDebugInfoIfNecessary cenv env m converted
| TDSuccess (args, n) ->
- let (TTarget(vars, rhs, _)) = tgs.[n]
+ let (TTarget(vars, rhs, _, _)) = tgs.[n]
// TAST stores pattern bindings in reverse order for some reason
// Reverse them here to give a good presentation to the user
let args = List.rev args
diff --git a/src/fsharp/SignatureConformance.fs b/src/fsharp/SignatureConformance.fs
index af0fe7fd5fe..583092b10f5 100644
--- a/src/fsharp/SignatureConformance.fs
+++ b/src/fsharp/SignatureConformance.fs
@@ -293,6 +293,15 @@ type Checker(g, amap, denv, remapInfo: SignatureRepackageInfo, checkingSig) =
warning(Error (FSComp.SR.ArgumentsInSigAndImplMismatch(sname.idText, iname.idText), iname.idRange))
| _ -> ()
+ let sigHasInlineIfLambda = HasFSharpAttribute g g.attrib_InlineIfLambdaAttribute sigArgInfo.Attribs
+ let implHasInlineIfLambda = HasFSharpAttribute g g.attrib_InlineIfLambdaAttribute implArgInfo.Attribs
+ let m =
+ match implArgInfo.Name with
+ | Some iname-> iname.idRange
+ | None -> implVal.Range
+ if sigHasInlineIfLambda && not implHasInlineIfLambda then
+ errorR(Error (FSComp.SR.implMissingInlineIfLambda(), m))
+
implArgInfo.Name <- sigArgInfo.Name
implArgInfo.Attribs <- attribs))) &&
diff --git a/src/fsharp/SyntaxTree.fs b/src/fsharp/SyntaxTree.fs
index 06e1b054ba0..2b0614261ed 100644
--- a/src/fsharp/SyntaxTree.fs
+++ b/src/fsharp/SyntaxTree.fs
@@ -175,11 +175,10 @@ type DebugPointForTarget =
[]
type DebugPointAtSequential =
- | Both
-
- | StmtOnly
-
- | ExprOnly
+ | SuppressNeither
+ | SuppressStmt
+ | SuppressBoth
+ | SuppressExpr
[]
type DebugPointAtTry =
@@ -509,13 +508,13 @@ type SynExpr =
range: range
| While of
- whileSeqPoint: DebugPointAtWhile *
+ whileDebugPoint: DebugPointAtWhile *
whileExpr: SynExpr *
doExpr: SynExpr *
range: range
| For of
- forSeqPoint: DebugPointAtFor *
+ forDebugPoint: DebugPointAtFor *
ident: Ident *
identBody: SynExpr *
direction: bool *
@@ -524,7 +523,7 @@ type SynExpr =
range: range
| ForEach of
- forSeqPoint: DebugPointAtFor *
+ forDebugPoint: DebugPointAtFor *
seqExprOnly: SeqExprOnly *
isFromSource: bool *
pat: SynPat *
@@ -555,11 +554,11 @@ type SynExpr =
isExnMatch: bool *
keywordRange: range *
matchClauses: SynMatchClause list *
- matchSeqPoint: DebugPointAtBinding *
+ matchDebugPoint: DebugPointAtBinding *
range: range
| Match of
- matchSeqPoint: DebugPointAtBinding *
+ matchDebugPoint: DebugPointAtBinding *
expr: SynExpr *
clauses: SynMatchClause list *
range: range
@@ -601,22 +600,22 @@ type SynExpr =
withCases: SynMatchClause list *
withRange: range *
range: range *
- trySeqPoint: DebugPointAtTry *
- withSeqPoint: DebugPointAtWith
+ tryDebugPoint: DebugPointAtTry *
+ withDebugPoint: DebugPointAtWith
| TryFinally of
tryExpr: SynExpr *
finallyExpr: SynExpr *
range: range *
- trySeqPoint: DebugPointAtTry *
- finallySeqPoint: DebugPointAtFinally
+ tryDebugPoint: DebugPointAtTry *
+ finallyDebugPoint: DebugPointAtFinally
| Lazy of
expr: SynExpr *
range: range
| Sequential of
- seqPoint: DebugPointAtSequential *
+ debugPoint: DebugPointAtSequential *
isTrueSeq: bool *
expr1: SynExpr *
expr2: SynExpr *
@@ -737,7 +736,7 @@ type SynExpr =
range: range
| SequentialOrImplicitYield of
- seqPoint:DebugPointAtSequential *
+ debugPoint:DebugPointAtSequential *
expr1:SynExpr *
expr2:SynExpr *
ifNotStmt:SynExpr *
@@ -754,7 +753,7 @@ type SynExpr =
range: range
| LetOrUseBang of
- bindSeqPoint: DebugPointAtBinding *
+ bindDebugPoint: DebugPointAtBinding *
isUse: bool *
isFromSource: bool *
pat: SynPat *
@@ -764,7 +763,7 @@ type SynExpr =
range: range
| MatchBang of
- matchSeqPoint: DebugPointAtBinding *
+ matchDebugPoint: DebugPointAtBinding *
expr: SynExpr *
clauses: SynMatchClause list *
range: range
@@ -948,8 +947,8 @@ type SynSimplePat =
ident: Ident *
altNameRefCell: SynSimplePatAlternativeIdInfo ref option *
isCompilerGenerated: bool *
- isThisVar: bool *
- isOptArg: bool *
+ isThisVal: bool *
+ isOptional: bool *
range: range
| Typed of
@@ -1024,7 +1023,7 @@ type SynPat =
| Named of
ident: Ident *
- isSelfIdentifier: bool *
+ isThisVal: bool *
accessibility: SynAccess option *
range: range
@@ -1146,7 +1145,7 @@ type SynMatchClause =
whenExpr: SynExpr option *
resultExpr: SynExpr *
range: range *
- spInfo: DebugPointForTarget
+ debugPoint: DebugPointForTarget
member this.RangeOfGuardAndRhs =
match this with
@@ -1196,7 +1195,7 @@ type SynBinding =
| SynBinding of
accessibility: SynAccess option *
kind: SynBindingKind *
- mustInline: bool *
+ isInline: bool *
isMutable: bool *
attributes: SynAttributes *
xmlDoc: PreXmlDoc *
@@ -1205,7 +1204,7 @@ type SynBinding =
returnInfo: SynBindingReturnInfo option *
expr: SynExpr *
range: range *
- seqPoint: DebugPointAtBinding
+ debugPoint: DebugPointAtBinding
// no member just named "Range", as that would be confusing:
// - for everything else, the 'range' member that appears last/second-to-last is the 'full range' of the whole tree construct
@@ -1499,6 +1498,8 @@ type SynArgInfo =
member x.Ident : Ident option = let (SynArgInfo(_,_,id)) = x in id
+ member x.Attributes : SynAttributes = let (SynArgInfo(attrs,_,_)) = x in attrs
+
[]
type SynValTyparDecls =
| SynValTyparDecls of
@@ -1673,7 +1674,7 @@ type SynModuleDecl =
range: range
| DoExpr of
- spInfo: DebugPointAtBinding *
+ debugPoint: DebugPointAtBinding *
expr: SynExpr *
range: range
diff --git a/src/fsharp/SyntaxTree.fsi b/src/fsharp/SyntaxTree.fsi
index 4a82c325a5f..88159770cfd 100644
--- a/src/fsharp/SyntaxTree.fsi
+++ b/src/fsharp/SyntaxTree.fsi
@@ -218,18 +218,21 @@ type DebugPointForTarget =
| Yes
| No
-/// Represents whether a debug point should be present for either the
+/// Represents whether a debug point should be suppressed for either the
/// first or second part of a sequential execution, that is whether the
/// construct corresponds to a debug point in the original source.
[]
type DebugPointAtSequential =
- | Both
+ | SuppressNeither
// This means "suppress a in 'a;b'" and "suppress b in 'a before b'"
- | StmtOnly
+ | SuppressStmt
+
+ // This means "suppress both"
+ | SuppressBoth
// This means "suppress b in 'a;b'" and "suppress a in 'a before b'"
- | ExprOnly
+ | SuppressExpr
/// Represents whether a debug point should be present for a 'try', that is whether
/// the construct corresponds to a debug point in the original source.
@@ -615,14 +618,14 @@ type SynExpr =
/// F# syntax: 'while ... do ...'
| While of
- whileSeqPoint: DebugPointAtWhile *
+ whileDebugPoint: DebugPointAtWhile *
whileExpr: SynExpr *
doExpr: SynExpr *
range: range
/// F# syntax: 'for i = ... to ... do ...'
| For of
- forSeqPoint: DebugPointAtFor *
+ forDebugPoint: DebugPointAtFor *
ident: Ident *
identBody: SynExpr *
direction: bool *
@@ -632,7 +635,7 @@ type SynExpr =
/// F# syntax: 'for ... in ... do ...'
| ForEach of
- forSeqPoint: DebugPointAtFor *
+ forDebugPoint: DebugPointAtFor *
seqExprOnly: SeqExprOnly *
isFromSource: bool *
pat: SynPat *
@@ -672,12 +675,12 @@ type SynExpr =
isExnMatch: bool *
keywordRange: range *
matchClauses: SynMatchClause list *
- matchSeqPoint: DebugPointAtBinding *
+ matchDebugPoint: DebugPointAtBinding *
range: range
/// F# syntax: match expr with pat1 -> expr | ... | patN -> exprN
| Match of
- matchSeqPoint: DebugPointAtBinding *
+ matchDebugPoint: DebugPointAtBinding *
expr: SynExpr *
clauses: SynMatchClause list *
range: range
@@ -732,16 +735,16 @@ type SynExpr =
withCases: SynMatchClause list *
withRange: range *
range: range *
- trySeqPoint: DebugPointAtTry *
- withSeqPoint: DebugPointAtWith
+ tryDebugPoint: DebugPointAtTry *
+ withDebugPoint: DebugPointAtWith
/// F# syntax: try expr finally expr
| TryFinally of
tryExpr: SynExpr *
finallyExpr: SynExpr *
range: range *
- trySeqPoint: DebugPointAtTry *
- finallySeqPoint: DebugPointAtFinally
+ tryDebugPoint: DebugPointAtTry *
+ finallyDebugPoint: DebugPointAtFinally
/// F# syntax: lazy expr
| Lazy of
@@ -752,7 +755,7 @@ type SynExpr =
///
/// isTrueSeq: false indicates "let v = a in b; v"
| Sequential of
- seqPoint: DebugPointAtSequential *
+ debugPoint: DebugPointAtSequential *
isTrueSeq: bool *
expr1: SynExpr *
expr2: SynExpr *
@@ -902,7 +905,7 @@ type SynExpr =
/// Used internally during type checking for translating computation expressions.
| SequentialOrImplicitYield of
- seqPoint:DebugPointAtSequential *
+ debugPoint:DebugPointAtSequential *
expr1:SynExpr *
expr2:SynExpr *
ifNotStmt:SynExpr *
@@ -929,7 +932,7 @@ type SynExpr =
/// F# syntax: let! pat = expr and! ... and! ... and! pat = expr in expr
/// Computation expressions only
| LetOrUseBang of
- bindSeqPoint: DebugPointAtBinding *
+ bindDebugPoint: DebugPointAtBinding *
isUse: bool *
isFromSource: bool *
pat: SynPat *
@@ -940,7 +943,7 @@ type SynExpr =
/// F# syntax: match! expr with pat1 -> expr | ... | patN -> exprN
| MatchBang of
- matchSeqPoint: DebugPointAtBinding *
+ matchDebugPoint: DebugPointAtBinding *
expr: SynExpr *
clauses: SynMatchClause list *
range: range
@@ -1062,14 +1065,14 @@ type SynSimplePat =
/// name of the identifier is already bound.
///
/// isCompilerGenerated: true if a compiler generated name
- /// isThisVar: true if 'this' variable in member
- /// isOptArg: true if a '?' is in front of the name
+ /// isThisVal: true if 'this' variable in member
+ /// isOptional: true if a '?' is in front of the name
| Id of
ident: Ident *
altNameRefCell: SynSimplePatAlternativeIdInfo ref option *
isCompilerGenerated: bool *
- isThisVar: bool *
- isOptArg: bool *
+ isThisVal: bool *
+ isOptional: bool *
range: range
/// A type annotated simple pattern
@@ -1151,11 +1154,10 @@ type SynPat =
| Wild of
range: range
- /// A name pattern 'ident' but @dsyme wants to keep the old name "named"
- /// when this double-purposed to also represent 'pat as ident' to reduce churn
+ /// A name pattern 'ident'
| Named of
ident: Ident *
- isSelfIdentifier: bool *
+ isThisVal: bool *
accessibility: SynAccess option *
range: range
@@ -1276,7 +1278,7 @@ type SynMatchClause =
whenExpr: SynExpr option *
resultExpr: SynExpr *
range: range *
- spInfo: DebugPointForTarget
+ debugPoint: DebugPointForTarget
/// Gets the syntax range of part of this construct
member RangeOfGuardAndRhs: range
@@ -1332,7 +1334,7 @@ type SynBinding =
| SynBinding of
accessibility: SynAccess option *
kind: SynBindingKind *
- mustInline: bool *
+ isInline: bool *
isMutable: bool *
attributes: SynAttributes *
xmlDoc: PreXmlDoc *
@@ -1341,7 +1343,7 @@ type SynBinding =
returnInfo: SynBindingReturnInfo option *
expr: SynExpr *
range: range *
- seqPoint: DebugPointAtBinding
+ debugPoint: DebugPointAtBinding
// no member just named "Range", as that would be confusing:
// - for everything else, the 'range' member that appears last/second-to-last is the 'full range' of the whole tree construct
@@ -1670,6 +1672,8 @@ type SynArgInfo =
member Ident: Ident option
+ member Attributes: SynAttributes
+
/// Represents the names and other metadata for the type parameters for a member or function
[]
type SynValTyparDecls =
@@ -1858,7 +1862,7 @@ type SynModuleDecl =
/// A 'do expr' within a module
| DoExpr of
- spInfo: DebugPointAtBinding *
+ debugPoint: DebugPointAtBinding *
expr: SynExpr *
range: range
diff --git a/src/fsharp/SyntaxTreeOps.fs b/src/fsharp/SyntaxTreeOps.fs
index 35f238f00d7..b232a5fe567 100644
--- a/src/fsharp/SyntaxTreeOps.fs
+++ b/src/fsharp/SyntaxTreeOps.fs
@@ -508,8 +508,9 @@ module SynInfo =
/// For 'let' definitions, we infer syntactic argument information from the r.h.s. of a definition, if it
/// is an immediate 'fun ... -> ...' or 'function ...' expression. This is noted in the F# language specification.
- /// This does not apply to member definitions.
- let InferLambdaArgs origRhsExpr =
+ /// This does not apply to member definitions nor to returns with attributes
+ let InferLambdaArgs (retInfo: SynArgInfo) origRhsExpr =
+ if retInfo.Attributes.Length > 0 then [] else
let rec loop e =
match e with
| SynExpr.Lambda (false, _, spats, rest, _, _) ->
@@ -545,7 +546,7 @@ module SynInfo =
match memberFlagsOpt with
| None ->
- let infosForLambdaArgs = InferLambdaArgs origRhsExpr
+ let infosForLambdaArgs = InferLambdaArgs retInfo origRhsExpr
let infosForArgs = infosForExplicitArgs @ (if explicitArgsAreSimple then infosForLambdaArgs else [])
let infosForArgs = AdjustArgsForUnitElimination infosForArgs
SynValData(None, SynValInfo(infosForArgs, retInfo), None)
diff --git a/src/fsharp/SyntaxTreeOps.fsi b/src/fsharp/SyntaxTreeOps.fsi
index 106b7a61540..2d7fd843011 100644
--- a/src/fsharp/SyntaxTreeOps.fsi
+++ b/src/fsharp/SyntaxTreeOps.fsi
@@ -226,11 +226,6 @@ module SynInfo =
/// rather than member signatures.
val AdjustMemberArgs: memFlags:SynMemberKind -> infosForArgs:'a list list -> 'a list list
- /// For 'let' definitions, we infer syntactic argument information from the r.h.s. of a definition, if it
- /// is an immediate 'fun ... -> ...' or 'function ...' expression. This is noted in the F# language specification.
- /// This does not apply to member definitions.
- val InferLambdaArgs: origRhsExpr:SynExpr -> SynArgInfo list list
-
val InferSynReturnData: retInfo:SynReturnInfo option -> SynArgInfo
val emptySynValData: SynValData
diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs
index 87b1810fe34..c25d4ce8ecd 100755
--- a/src/fsharp/TcGlobals.fs
+++ b/src/fsharp/TcGlobals.fs
@@ -350,11 +350,10 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
let v_FormattableStringFactory_ty = mkNonGenericTy v_FormattableStringFactory_tcref
let v_string_ty = mkNonGenericTy v_string_tcr
let v_decimal_ty = mkSysNonGenericTy sys "Decimal"
- let v_unit_ty = mkNonGenericTy v_unit_tcr_nice
- let v_system_Type_ty = mkSysNonGenericTy sys "Type"
+ let v_unit_ty = mkNonGenericTy v_unit_tcr_nice
+ let v_system_Type_ty = mkSysNonGenericTy sys "Type"
let v_Array_tcref = findSysTyconRef sys "Array"
-
let v_system_Reflection_MethodInfo_ty = mkSysNonGenericTy ["System";"Reflection"] "MethodInfo"
let v_nullable_tcr = findSysTyconRef sys "Nullable`1"
@@ -461,6 +460,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
let fslib_MFStringModule_nleref = mkNestedNonLocalEntityRef fslib_MFCollections_nleref "StringModule"
let fslib_MFNativePtrModule_nleref = mkNestedNonLocalEntityRef fslib_MFNativeInterop_nleref "NativePtrModule"
let fslib_MFOptionModule_nleref = mkNestedNonLocalEntityRef fslib_MFCore_nleref "OptionModule"
+ let fslib_MFStateMachineHelpers_nleref = mkNestedNonLocalEntityRef fslib_MFCompilerServices_nleref "StateMachineHelpers"
let fslib_MFRuntimeHelpers_nleref = mkNestedNonLocalEntityRef fslib_MFCompilerServices_nleref "RuntimeHelpers"
let fslib_MFQuotations_nleref = mkNestedNonLocalEntityRef fslib_MF_nleref "Quotations"
@@ -517,15 +517,16 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
fslib_MFPrintfModule_nleref
fslib_MFSeqModule_nleref
fslib_MFListModule_nleref
- fslib_MFArrayModule_nleref
- fslib_MFArray2DModule_nleref
- fslib_MFArray3DModule_nleref
- fslib_MFArray4DModule_nleref
- fslib_MFSetModule_nleref
- fslib_MFMapModule_nleref
- fslib_MFStringModule_nleref
- fslib_MFNativePtrModule_nleref
- fslib_MFOptionModule_nleref
+ fslib_MFArrayModule_nleref
+ fslib_MFArray2DModule_nleref
+ fslib_MFArray3DModule_nleref
+ fslib_MFArray4DModule_nleref
+ fslib_MFSetModule_nleref
+ fslib_MFMapModule_nleref
+ fslib_MFStringModule_nleref
+ fslib_MFNativePtrModule_nleref
+ fslib_MFOptionModule_nleref
+ fslib_MFStateMachineHelpers_nleref
fslib_MFRuntimeHelpers_nleref ] do
yield nleref.LastItemMangledName, ERefNonLocal nleref ]
@@ -719,7 +720,11 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
let v_seq_finally_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "EnumerateThenFinally" , None , None , [varb], ([[mkSeqTy varbTy]; [v_unit_ty --> v_unit_ty]], mkSeqTy varbTy))
let v_seq_of_functions_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "EnumerateFromFunctions" , None , None , [vara;varb], ([[v_unit_ty --> varaTy]; [varaTy --> v_bool_ty]; [varaTy --> varbTy]], mkSeqTy varbTy))
let v_create_event_info = makeIntrinsicValRef(fslib_MFRuntimeHelpers_nleref, "CreateEvent" , None , None , [vara;varb], ([[varaTy --> v_unit_ty]; [varaTy --> v_unit_ty]; [(v_obj_ty --> (varbTy --> v_unit_ty)) --> varaTy]], TType_app (v_fslib_IEvent2_tcr, [varaTy;varbTy])))
- let v_seq_to_array_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "toArray" , None , Some "ToArray", [varb], ([[mkSeqTy varbTy]], mkArrayType 1 varbTy))
+ let v_cgh__useResumableCode_info = makeIntrinsicValRef(fslib_MFStateMachineHelpers_nleref, "__useResumableCode" , None , None , [vara], ([[]], v_bool_ty))
+ let v_cgh__resumeAt_info = makeIntrinsicValRef(fslib_MFStateMachineHelpers_nleref, "__resumeAt" , None , None , [vara], ([[v_int_ty]; [varaTy]], varaTy))
+ let v_cgh__stateMachine_info = makeIntrinsicValRef(fslib_MFStateMachineHelpers_nleref, "__stateMachine" , None , None , [vara; varb], ([[varaTy]], varbTy)) // inaccurate type but it doesn't matter for linking
+ let v_cgh__resumableEntry_info = makeIntrinsicValRef(fslib_MFStateMachineHelpers_nleref, "__resumableEntry" , None , None , [vara], ([[v_int_ty --> varaTy]; [v_unit_ty --> varaTy]], varaTy))
+ let v_seq_to_array_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "toArray" , None , Some "ToArray", [varb], ([[mkSeqTy varbTy]], mkArrayType 1 varbTy))
let v_seq_to_list_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "toList" , None , Some "ToList" , [varb], ([[mkSeqTy varbTy]], mkListTy varbTy))
let v_seq_map_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "map" , None , Some "Map" , [vara;varb], ([[varaTy --> varbTy]; [mkSeqTy varaTy]], mkSeqTy varbTy))
let v_seq_singleton_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "singleton" , None , Some "Singleton" , [vara], ([[varaTy]], mkSeqTy varaTy))
@@ -1003,6 +1008,10 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
member val ListCollector_tcr = mk_MFCompilerServices_tcref fslibCcu "ListCollector`1"
member val ArrayCollector_tcr = mk_MFCompilerServices_tcref fslibCcu "ArrayCollector`1"
member g.mk_GeneratedSequenceBase_ty seqElemTy = TType_app(g.seq_base_tcr,[seqElemTy])
+ member val ResumableStateMachine_tcr = mk_MFCompilerServices_tcref fslibCcu "ResumableStateMachine`1"
+ member g.mk_ResumableStateMachine_ty dataTy = TType_app(g.ResumableStateMachine_tcr,[dataTy])
+ member val IResumableStateMachine_tcr = mk_MFCompilerServices_tcref fslibCcu "IResumableStateMachine`1"
+ member g.mk_IResumableStateMachine_ty dataTy = TType_app(g.IResumableStateMachine_tcr,[dataTy])
member g.mk_ListCollector_ty seqElemTy = TType_app(g.ListCollector_tcr,[seqElemTy])
member g.mk_ArrayCollector_ty seqElemTy = TType_app(g.ArrayCollector_tcr,[seqElemTy])
member val byrefkind_In_tcr = mkNonLocalTyconRef fslib_MFByRefKinds_nleref "In"
@@ -1011,6 +1020,8 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
member val measureproduct_tcr = mk_MFCompilerServices_tcref fslibCcu "MeasureProduct`2"
member val measureinverse_tcr = mk_MFCompilerServices_tcref fslibCcu "MeasureInverse`1"
member val measureone_tcr = mk_MFCompilerServices_tcref fslibCcu "MeasureOne"
+ member val ResumableCode_tcr = mk_MFCompilerServices_tcref fslibCcu "ResumableCode`2"
+
member _.il_arr_tcr_map = v_il_arr_tcr_map
member _.ref_tuple1_tcr = v_ref_tuple1_tcr
member _.ref_tuple2_tcr = v_ref_tuple2_tcr
@@ -1105,7 +1116,8 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
tryMkSysNonGenericTy ["System"; "Runtime"; "ExceptionServices"] "ExceptionDispatchInfo"
member _.system_Reflection_MethodInfo_ty = v_system_Reflection_MethodInfo_ty
-
+ member _.mk_IAsyncStateMachine_ty = mkSysNonGenericTy sysCompilerServices "IAsyncStateMachine"
+
member val system_Array_tcref = v_Array_tcref
member val system_Object_tcref = findSysTyconRef sys "Object"
member val system_Value_tcref = findSysTyconRef sys "ValueType"
@@ -1234,6 +1246,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
member val attrib_CompilationArgumentCountsAttribute = mk_MFCore_attrib "CompilationArgumentCountsAttribute"
member val attrib_CompilationMappingAttribute = mk_MFCore_attrib "CompilationMappingAttribute"
member val attrib_CLIEventAttribute = mk_MFCore_attrib "CLIEventAttribute"
+ member val attrib_InlineIfLambdaAttribute = mk_MFCore_attrib "InlineIfLambdaAttribute"
member val attrib_CLIMutableAttribute = mk_MFCore_attrib "CLIMutableAttribute"
member val attrib_AllowNullLiteralAttribute = mk_MFCore_attrib "AllowNullLiteralAttribute"
member val attrib_NoEqualityAttribute = mk_MFCore_attrib "NoEqualityAttribute"
@@ -1492,6 +1505,11 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
member _.quote_to_linq_lambda_info = v_quote_to_linq_lambda_info
+ member val cgh__stateMachine_vref = ValRefForIntrinsic v_cgh__stateMachine_info
+ member val cgh__useResumableCode_vref = ValRefForIntrinsic v_cgh__useResumableCode_info
+ member val cgh__resumeAt_vref = ValRefForIntrinsic v_cgh__resumeAt_info
+ member val cgh__resumableEntry_vref = ValRefForIntrinsic v_cgh__resumableEntry_info
+
member val generic_hash_withc_tuple2_vref = ValRefForIntrinsic v_generic_hash_withc_tuple2_info
member val generic_hash_withc_tuple3_vref = ValRefForIntrinsic v_generic_hash_withc_tuple3_info
member val generic_hash_withc_tuple4_vref = ValRefForIntrinsic v_generic_hash_withc_tuple4_info
diff --git a/src/fsharp/TypedTree.fs b/src/fsharp/TypedTree.fs
index 3798883baee..f6cc7a0eb12 100644
--- a/src/fsharp/TypedTree.fs
+++ b/src/fsharp/TypedTree.fs
@@ -39,9 +39,6 @@ type StampMap<'T> = Map
[]
type ValInline =
- /// Indicates the value must always be inlined and no .NET IL code is generated for the value/function
- | PseudoVal
-
/// Indicates the value is inlined but the .NET IL code for the function still exists, e.g. to satisfy interfaces on objects, but that it is also always inlined
| Always
@@ -54,7 +51,7 @@ type ValInline =
/// Returns true if the implementation of a value must always be inlined
member x.MustInline =
match x with
- | ValInline.PseudoVal | ValInline.Always -> true
+ | ValInline.Always -> true
| ValInline.Optional | ValInline.Never -> false
/// A flag associated with values that indicates whether the recursive scope of the value is currently being processed, and
@@ -110,7 +107,6 @@ type ValFlags(flags: int64) =
(if isCompGen then 0b00000000000000001000L
else 0b000000000000000000000L) |||
(match inlineInfo with
- | ValInline.PseudoVal -> 0b00000000000000000000L
| ValInline.Always -> 0b00000000000000010000L
| ValInline.Optional -> 0b00000000000000100000L
| ValInline.Never -> 0b00000000000000110000L) |||
@@ -142,7 +138,7 @@ type ValFlags(flags: int64) =
(match isGeneratedEventVal with
| false -> 0b00000000000000000000L
- | true -> 0b00100000000000000000L)
+ | true -> 0b00100000000000000000L)
ValFlags flags
@@ -167,7 +163,7 @@ type ValFlags(flags: int64) =
member x.InlineInfo =
match (flags &&& 0b00000000000000110000L) with
- | 0b00000000000000000000L -> ValInline.PseudoVal
+ | 0b00000000000000000000L
| 0b00000000000000010000L -> ValInline.Always
| 0b00000000000000100000L -> ValInline.Optional
| 0b00000000000000110000L -> ValInline.Never
@@ -204,11 +200,11 @@ type ValFlags(flags: int64) =
member x.WithRecursiveValInfo recValInfo =
let flags =
- (flags &&& ~~~0b00000001100000000000L) |||
+ (flags &&& ~~~0b00000001100000000000L) |||
(match recValInfo with
- | ValNotInRecScope -> 0b00000000000000000000L
- | ValInRecScope true -> 0b00000000100000000000L
- | ValInRecScope false -> 0b00000001000000000000L)
+ | ValNotInRecScope -> 0b00000000000000000000L
+ | ValInRecScope true -> 0b00000000100000000000L
+ | ValInRecScope false -> 0b00000001000000000000L)
ValFlags flags
member x.MakesNoCriticalTailcalls = (flags &&& 0b00000010000000000000L) <> 0L
@@ -235,13 +231,17 @@ type ValFlags(flags: int64) =
member x.WithIgnoresByrefScope = ValFlags(flags ||| 0b10000000000000000000L)
+ member x.InlineIfLambda = (flags &&& 0b100000000000000000000L) <> 0L
+
+ member x.WithInlineIfLambda = ValFlags(flags ||| 0b100000000000000000000L)
+
/// Get the flags as included in the F# binary metadata
member x.PickledBits =
// Clear the RecursiveValInfo, only used during inference and irrelevant across assembly boundaries
// Clear the IsCompiledAsStaticPropertyWithoutField, only used to determine whether to use a true field for a value, and to eliminate the optimization info for observable bindings
// Clear the HasBeenReferenced, only used to report "unreferenced variable" warnings and to help collect 'it' values in FSI.EXE
// Clear the IsGeneratedEventVal, since there's no use in propagating specialname information for generated add/remove event vals
- (flags &&& ~~~0b10011001100000000000L)
+ (flags &&& ~~~0b010011001100000000000L)
/// Represents the kind of a type parameter
[]
@@ -2707,6 +2707,9 @@ type Val =
/// Get the inline declaration on the value
member x.InlineInfo = x.val_flags.InlineInfo
+ /// Get the inline declaration on a parameter or other non-function-declaration value, used for optimization
+ member x.InlineIfLambda = x.val_flags.InlineIfLambda
+
/// Indicates whether the inline declaration for the value indicate that the value must be inlined?
member x.MustInline = x.InlineInfo.MustInline
@@ -2916,6 +2919,8 @@ type Val =
member x.SetIgnoresByrefScope() = x.val_flags <- x.val_flags.WithIgnoresByrefScope
+ member x.SetInlineIfLambda() = x.val_flags <- x.val_flags.WithInlineIfLambda
+
member x.SetValReprInfo info =
match x.val_opt_data with
| Some optData -> optData.val_repr_info <- info
@@ -3777,6 +3782,9 @@ type ValRef =
/// Get the inline declaration on the value
member x.InlineInfo = x.Deref.InlineInfo
+ /// Get the inline declaration on a parameter or other non-function-declaration value, used for optimization
+ member x.InlineIfLambda = x.Deref.InlineIfLambda
+
/// Indicates whether the inline declaration for the value indicate that the value must be inlined?
member x.MustInline = x.Deref.MustInline
@@ -4306,22 +4314,38 @@ type DecisionTreeTest =
override x.ToString() = sprintf "%+A" x
/// A target of a decision tree. Can be thought of as a little function, though is compiled as a local block.
+/// -- boundVals - The values bound at the target, matching the valuesin the TDSuccess
+/// -- targetExpr - The expression to evaluate if we branch to the target
+/// -- debugPoint - The debug point for the target
+/// -- isStateVarFlags - Indicates which, if any, of the values are repesents as state machine variables
[]
type DecisionTreeTarget =
- | TTarget of Val list * Expr * DebugPointForTarget
+ | TTarget of
+ boundVals: Val list *
+ targetExpr: Expr *
+ debugPoint: DebugPointForTarget *
+ isStateVarFlags: bool list option
[]
member x.DebugText = x.ToString()
+ member x.TargetExpression = (let (TTarget(_, expr, _, _)) = x in expr)
+
override x.ToString() = sprintf "DecisionTreeTarget(...)"
/// A collection of simultaneous bindings
type Bindings = Binding list
/// A binding of a variable to an expression, as in a `let` binding or similar
+/// -- val: The value being bound
+/// -- expr: The expression to execute to get the value
+/// -- debugPoint: The debug point for the binding
[]
type Binding =
- | TBind of var: Val * expr: Expr * debugPoint: DebugPointAtBinding
+ | TBind of
+ var: Val *
+ expr: Expr *
+ debugPoint: DebugPointAtBinding
/// The value being bound
member x.Var = (let (TBind(v, _, _)) = x in v)
@@ -4341,7 +4365,11 @@ type Binding =
/// integer indicates which choice in the target set is being selected by this item.
[]
type ActivePatternElemRef =
- | APElemRef of activePatternInfo: ActivePatternInfo * activePatternVal: ValRef * caseIndex: int * isStructRetTy: bool
+ | APElemRef of
+ activePatternInfo: ActivePatternInfo *
+ activePatternVal: ValRef *
+ caseIndex: int *
+ isStructRetTy: bool
/// Get the full information about the active pattern being referred to
member x.ActivePatternInfo = (let (APElemRef(info, _, _, _)) = x in info)
@@ -4365,7 +4393,10 @@ type ActivePatternElemRef =
[]
type ValReprInfo =
/// ValReprInfo (typars, args, result)
- | ValReprInfo of typars: TyparReprInfo list * args: ArgReprInfo list list * result: ArgReprInfo
+ | ValReprInfo of
+ typars: TyparReprInfo list *
+ args: ArgReprInfo list list *
+ result: ArgReprInfo
/// Get the extra information about the arguments for the value
member x.ArgInfos = (let (ValReprInfo(_, args, _)) = x in args)
@@ -4748,7 +4779,7 @@ type TOp =
[]
member x.DebugText = x.ToString()
-
+
override op.ToString() =
match op with
| UnionCase ucref -> "UnionCase(" + ucref.CaseName + ")"
diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs
index 9fd50c3f059..c90b5175425 100644
--- a/src/fsharp/TypedTreeOps.fs
+++ b/src/fsharp/TypedTreeOps.fs
@@ -1204,7 +1204,7 @@ type MatchBuilder(spBind, inpRange: range) =
targets.Add tg
n
- member x.AddResultTarget(e, spTarget) = TDSuccess([], x.AddTarget(TTarget([], e, spTarget)))
+ member x.AddResultTarget(e, spTarget) = TDSuccess([], x.AddTarget(TTarget([], e, spTarget, None)))
member x.CloseTargets() = targets |> ResizeArray.toList
@@ -1786,6 +1786,8 @@ let isInterfaceTy g ty =
| ILTypeMetadata (TILObjectReprData(_, _, td)) -> td.IsInterface
| FSharpOrArrayOrByrefOrTupleOrExnTypeMetadata -> isFSharpInterfaceTy g ty
+let isFSharpDelegateTy g ty = isDelegateTy g ty && isFSharpObjModelTy g ty
+
let isClassTy g ty =
match metadataOfTy g ty with
#if !NO_EXTENSIONTYPING
@@ -3067,6 +3069,7 @@ let IsMatchingFSharpAttributeOpt g attrOpt (Attrib(tcref2, _, _, _, _, _, _)) =
let (|ExtractAttribNamedArg|_|) nm args =
args |> List.tryPick (function (AttribNamedArg(nm2, _, _, v)) when nm = nm2 -> Some v | _ -> None)
+let (|StringExpr|_|) = function Expr.Const (Const.String n, _, _) -> Some n | _ -> None
let (|AttribInt32Arg|_|) = function AttribExpr(_, Expr.Const (Const.Int32 n, _, _)) -> Some n | _ -> None
let (|AttribInt16Arg|_|) = function AttribExpr(_, Expr.Const (Const.Int16 n, _, _)) -> Some n | _ -> None
let (|AttribBoolArg|_|) = function AttribExpr(_, Expr.Const (Const.Bool n, _, _)) -> Some n | _ -> None
@@ -4108,7 +4111,7 @@ module DebugPrint =
| (DecisionTreeTest.ActivePatternCase (exp, _, _, _, _, _)) -> wordL(tagText "query") ^^ exprL g exp
| (DecisionTreeTest.Error _) -> wordL (tagText "error recovery")
- and targetL g i (TTarget (argvs, body, _)) =
+ and targetL g i (TTarget (argvs, body, _, _)) =
leftL(tagText "T") ^^ intL i ^^ tupleL (flatValsL argvs) ^^ rightL(tagText ":") --- exprL g body
and flatValsL vs = vs |> List.map valL
@@ -4479,11 +4482,11 @@ let freeTyvarsAllPublic tyvars =
let (|LinearMatchExpr|_|) expr =
match expr with
- | Expr.Match (sp, m, dtree, [|tg1;(TTarget([], e2, sp2))|], m2, ty) -> Some(sp, m, dtree, tg1, e2, sp2, m2, ty)
+ | Expr.Match (sp, m, dtree, [|tg1;(TTarget([], e2, sp2, _))|], m2, ty) -> Some(sp, m, dtree, tg1, e2, sp2, m2, ty)
| _ -> None
let rebuildLinearMatchExpr (sp, m, dtree, tg1, e2, sp2, m2, ty) =
- primMkMatch (sp, m, dtree, [|tg1;(TTarget([], e2, sp2))|], m2, ty)
+ primMkMatch (sp, m, dtree, [|tg1;(TTarget([], e2, sp2, None))|], m2, ty)
/// Detect a subset of 'Expr.Op' expressions we process in a linear way (i.e. using tailcalls, rather than
/// unbounded stack). Only covers Cons(args,Cons(args,Cons(args,Cons(args,...._)))).
@@ -4844,8 +4847,10 @@ and accFreeInOp opts op acc =
and accFreeInTargets opts targets acc =
Array.foldBack (accFreeInTarget opts) targets acc
-and accFreeInTarget opts (TTarget(vs, expr, _)) acc =
- List.foldBack (boundLocalVal opts) vs (accFreeInExpr opts expr acc)
+and accFreeInTarget opts (TTarget(vs, expr, _, flags)) acc =
+ match flags with
+ | None -> List.foldBack (boundLocalVal opts) vs (accFreeInExpr opts expr acc)
+ | Some xs -> List.foldBack2 (fun v isStateVar acc -> if isStateVar then acc else boundLocalVal opts v acc) vs xs (accFreeInExpr opts expr acc)
and accFreeInFlatExprs opts (exprs: Exprs) acc = List.foldBack (accFreeInExpr opts) exprs acc
@@ -5214,12 +5219,7 @@ and remapExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) expr =
// Binding constructs - see also dtrees below
| Expr.Lambda (_, ctorThisValOpt, baseValOpt, vs, b, m, rty) ->
- let ctorThisValOpt, tmenv = Option.mapFold (copyAndRemapAndBindVal g compgen) tmenv ctorThisValOpt
- let baseValOpt, tmenv = Option.mapFold (copyAndRemapAndBindVal g compgen) tmenv baseValOpt
- let vs, tmenv = copyAndRemapAndBindVals g compgen tmenv vs
- let b = remapExpr g compgen tmenv b
- let rty = remapType tmenv rty
- Expr.Lambda (newUnique(), ctorThisValOpt, baseValOpt, vs, b, m, rty)
+ remapLambaExpr g compgen tmenv (ctorThisValOpt, baseValOpt, vs, b, m, rty)
| Expr.TyLambda (_, tps, b, m, rty) ->
let tps', tmenvinner = tmenvCopyRemapAndBindTypars (remapAttribs g tmenv) tmenv tps
@@ -5245,14 +5245,7 @@ and remapExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) expr =
else Expr.Val (vr', vf', m)
| Expr.Quote (a, dataCell, isFromQueryExpression, m, ty) ->
- let doData (typeDefs, argTypes, argExprs, res) = (typeDefs, remapTypesAux tmenv argTypes, remapExprs g compgen tmenv argExprs, res)
- let data' =
- match dataCell.Value with
- | None -> None
- | Some (data1, data2) -> Some (doData data1, doData data2)
- // fix value of compgen for both original expression and pickled AST
- let compgen = fixValCopyFlagForQuotations compgen
- Expr.Quote (remapExpr g compgen tmenv a, ref data', isFromQueryExpression, m, remapType tmenv ty)
+ remapQuoteExpr g compgen tmenv (a, dataCell, isFromQueryExpression, m, ty)
| Expr.Obj (_, ty, basev, basecall, overrides, iimpls, m) ->
let basev', tmenvinner = Option.mapFold (copyAndRemapAndBindVal g compgen) tmenv basev
@@ -5285,19 +5278,10 @@ and remapExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) expr =
mkCompGenLet m tmp (mkUnionCaseFieldGetProvenViaExprAddr (arg, uref, tinst, cidx, m)) (mkValAddr m readonly (mkLocalValRef tmp))
| Expr.Op (op, tinst, args, m) ->
- let op' = remapOp tmenv op
- let tinst' = remapTypes tmenv tinst
- let args' = remapExprs g compgen tmenv args
- if op === op' && tinst === tinst' && args === args' then expr
- else Expr.Op (op', tinst', args', m)
+ remapOpExpr g compgen tmenv (op, tinst, args, m) expr
| Expr.App (e1, e1ty, tyargs, args, m) ->
- let e1' = remapExpr g compgen tmenv e1
- let e1ty' = remapPossibleForallTy g tmenv e1ty
- let tyargs' = remapTypes tmenv tyargs
- let args' = remapExprs g compgen tmenv args
- if e1 === e1' && e1ty === e1ty' && tyargs === tyargs' && args === args' then expr
- else Expr.App (e1', e1ty', tyargs', args', m)
+ remapAppExpr g compgen tmenv (e1, e1ty, tyargs, args, m) expr
| Expr.Link eref ->
remapExpr g compgen tmenv !eref
@@ -5314,9 +5298,42 @@ and remapExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) expr =
let traitInfoR = remapTraitInfo tmenv traitInfo
Expr.WitnessArg (traitInfoR, m)
-and remapTarget g compgen tmenv (TTarget(vs, e, spTarget)) =
+and remapLambaExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) (ctorThisValOpt, baseValOpt, vs, b, m, rty) =
+ let ctorThisValOpt, tmenv = Option.mapFold (copyAndRemapAndBindVal g compgen) tmenv ctorThisValOpt
+ let baseValOpt, tmenv = Option.mapFold (copyAndRemapAndBindVal g compgen) tmenv baseValOpt
+ let vs, tmenv = copyAndRemapAndBindVals g compgen tmenv vs
+ let b = remapExpr g compgen tmenv b
+ let rty = remapType tmenv rty
+ Expr.Lambda (newUnique(), ctorThisValOpt, baseValOpt, vs, b, m, rty)
+
+and remapQuoteExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) (a, dataCell, isFromQueryExpression, m, ty) =
+ let doData (typeDefs, argTypes, argExprs, res) = (typeDefs, remapTypesAux tmenv argTypes, remapExprs g compgen tmenv argExprs, res)
+ let data' =
+ match dataCell.Value with
+ | None -> None
+ | Some (data1, data2) -> Some (doData data1, doData data2)
+ // fix value of compgen for both original expression and pickled AST
+ let compgen = fixValCopyFlagForQuotations compgen
+ Expr.Quote (remapExpr g compgen tmenv a, ref data', isFromQueryExpression, m, remapType tmenv ty)
+
+and remapOpExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) (op, tinst, args, m) origExpr =
+ let op' = remapOp tmenv op
+ let tinst' = remapTypes tmenv tinst
+ let args' = remapExprs g compgen tmenv args
+ if op === op' && tinst === tinst' && args === args' then origExpr
+ else Expr.Op (op', tinst', args', m)
+
+and remapAppExpr (g: TcGlobals) (compgen: ValCopyFlag) (tmenv: Remap) (e1, e1ty, tyargs, args, m) origExpr =
+ let e1' = remapExpr g compgen tmenv e1
+ let e1ty' = remapPossibleForallTy g tmenv e1ty
+ let tyargs' = remapTypes tmenv tyargs
+ let args' = remapExprs g compgen tmenv args
+ if e1 === e1' && e1ty === e1ty' && tyargs === tyargs' && args === args' then origExpr
+ else Expr.App (e1', e1ty', tyargs', args', m)
+
+and remapTarget g compgen tmenv (TTarget(vs, e, spTarget, flags)) =
let vs', tmenvinner = copyAndRemapAndBindVals g compgen tmenv vs
- TTarget(vs', remapExpr g compgen tmenvinner e, spTarget)
+ TTarget(vs', remapExpr g compgen tmenvinner e, spTarget, flags)
and remapLinearExpr g compgen tmenv expr contf =
@@ -5727,7 +5744,7 @@ let rec remarkExpr m x =
Expr.Let (remarkBind m bind, remarkExpr m e, m, fvs)
| Expr.Match (_, _, pt, targets, _, ty) ->
- let targetsR = targets |> Array.map (fun (TTarget(vs, e, _)) -> TTarget(vs, remarkExpr m e, DebugPointForTarget.No))
+ let targetsR = targets |> Array.map (fun (TTarget(vs, e, _, flags)) -> TTarget(vs, remarkExpr m e, DebugPointForTarget.No, flags))
primMkMatch (DebugPointAtBinding.NoneAtInvisible, m, remarkDecisionTree m pt, targetsR, m, ty)
| Expr.Val (x, valUseFlags, _) ->
@@ -5742,10 +5759,38 @@ let rec remarkExpr m x =
List.map (remarkInterfaceImpl m) iimpls, m)
| Expr.Op (op, tinst, args, _) ->
+
+ // If this is ultimately being inlined as the implementation
+ // of a 'while'/'for' etc in a computation expression then lay down a
+ // debug point
let op =
match op with
- | TOp.TryFinally (_, _) -> TOp.TryFinally (DebugPointAtTry.No, DebugPointAtFinally.No)
- | TOp.TryWith (_, _) -> TOp.TryWith (DebugPointAtTry.No, DebugPointAtWith.No)
+ | TOp.For (spFor, style) ->
+ let spFor2 =
+ match m.DebugPointKind with
+ | RangeDebugPointKind.For -> DebugPointAtFor.Yes m
+ | _ -> spFor
+ TOp.For(spFor2, style)
+ | TOp.While (spWhile, marker) ->
+ let spWhile2 =
+ match m.DebugPointKind with
+ | RangeDebugPointKind.While -> DebugPointAtWhile.Yes m
+ | _ -> spWhile
+ TOp.While(spWhile2, marker)
+ | TOp.TryFinally (spTry, _) ->
+ let spTry2 =
+ match m.DebugPointKind with
+ | RangeDebugPointKind.Try -> DebugPointAtTry.Yes m
+ | _ -> spTry
+ // The debug point for the 'finally' is not yet recoverable
+ TOp.TryFinally (spTry2, DebugPointAtFinally.No)
+ | TOp.TryWith (spTry, _) ->
+ let spTry2 =
+ match m.DebugPointKind with
+ | RangeDebugPointKind.Try -> DebugPointAtTry.Yes m
+ | _ -> spTry
+ // The debug point for the 'with' is not yet recoverable
+ TOp.TryWith (spTry2, DebugPointAtWith.No)
| _ -> op
Expr.Op (op, tinst, remarkExprs m args, m)
@@ -5757,8 +5802,31 @@ let rec remarkExpr m x =
| Expr.App (e1, e1ty, tyargs, args, _) ->
Expr.App (remarkExpr m e1, e1ty, tyargs, remarkExprs m args, m)
- | Expr.Sequential (e1, e2, dir, _, _) ->
- Expr.Sequential (remarkExpr m e1, remarkExpr m e2, dir, DebugPointAtSequential.StmtOnly, m)
+ | Expr.Sequential (e1, e2, dir, sp, _) ->
+ let e1R = remarkExpr m e1
+ let e2R = remarkExpr m e2
+
+ // This is to suppress false sequence points when inlining 'ResumableCode.TryWith'
+ // It is adhoc and may need to be improved if the library implementation changes
+ //
+ // For this specific code:
+ //
+ // __stack_caught <- true
+ // __stack_savedExn <- ...
+ //
+ // we suppress the sequence points on both the sequentials.
+ //
+ // Normally inlining associates all of the inlined code with the expression range of the overall construct,
+ // which for computation expression calls TryFinally and TryWith is the 'try' keyword.
+ // However this is broken when inlining sophisticated control flow as the 'with' and 'finally'
+ // parts of the code get the 'try' keyword as their sequence point range.
+ let sp =
+ match sp, e2R with
+ | DebugPointAtSequential.SuppressBoth, _ -> DebugPointAtSequential.SuppressBoth
+ | _, Expr.Op(TOp.LValueOp (LSet _, v), _, _, _) when v.DisplayName = "__stack_savedExn" -> DebugPointAtSequential.SuppressBoth
+ | _ -> DebugPointAtSequential.SuppressStmt
+
+ Expr.Sequential (e1R, e2R, dir, sp, m)
| Expr.StaticOptimization (eqns, e2, e3, _) ->
Expr.StaticOptimization (eqns, remarkExpr m e2, remarkExpr m e3, m)
@@ -6124,7 +6192,7 @@ let foldLinearBindingTargetsOfMatch tree (targets: _[]) =
/// rebuild the targets, replacing linear targets by ones that include all the 'let' bindings from the source
let targets' =
- targets |> Array.mapi (fun i (TTarget(vs, exprTarget, spTarget) as tg) ->
+ targets |> Array.mapi (fun i (TTarget(vs, exprTarget, spTarget, _) as tg) ->
if isLinearTgtIdx i then
let (binds, es) = getLinearTgtIdx i
// The value bindings are moved to become part of the target.
@@ -6132,7 +6200,7 @@ let foldLinearBindingTargetsOfMatch tree (targets: _[]) =
let mTarget = exprTarget.Range
let es = es |> List.map (remarkExpr mTarget)
// These are non-sticky - any sequence point for 'exprTarget' goes on 'exprTarget' _after_ the bindings have been evaluated
- TTarget(List.empty, mkLetsBind mTarget binds (mkInvisibleLetsFromBindings mTarget vs es exprTarget), spTarget)
+ TTarget(List.empty, mkLetsBind mTarget binds (mkInvisibleLetsFromBindings mTarget vs es exprTarget), spTarget, None)
else tg )
tree', targets'
@@ -6143,7 +6211,7 @@ let rec simplifyTrivialMatch spBind exprm matchm ty tree (targets : _[]) =
| TDSuccess(es, n) ->
if n >= targets.Length then failwith "simplifyTrivialMatch: target out of range"
// REVIEW: should we use _spTarget here?
- let (TTarget(vs, rhs, _spTarget)) = targets.[n]
+ let (TTarget(vs, rhs, _spTarget, _)) = targets.[n]
if vs.Length <> es.Length then failwith ("simplifyTrivialMatch: invalid argument, n = " + string n + ", List.length targets = " + string targets.Length)
// These are non-sticky - any sequence point for 'rhs' goes on 'rhs' _after_ the bindings have been made
mkInvisibleLetsFromBindings rhs.Range vs es rhs
@@ -6667,7 +6735,7 @@ type ExprFolders<'State> (folders: ExprFolder<'State>) =
match folders.targetIntercept exprFClosure z x with
| Some z -> z // intercepted
| None -> // structurally recurse
- let (TTarget (_, body, _)) = x
+ let (TTarget (_, body, _, _)) = x
exprF z body
and tmethodF z x =
@@ -6776,7 +6844,11 @@ let mkRefCellContentsRef (g: TcGlobals) = mkRecdFieldRef g.refcell_tcr_canon "co
let mkSequential spSeq m e1 e2 = Expr.Sequential (e1, e2, NormalSeq, spSeq, m)
-let mkCompGenSequential m e1 e2 = mkSequential DebugPointAtSequential.StmtOnly m e1 e2
+let mkCompGenSequential m stmt expr = mkSequential DebugPointAtSequential.SuppressStmt m stmt expr
+
+let mkThenDoSequential spSeq m expr stmt = Expr.Sequential (expr, stmt, ThenDoSeq, spSeq, m)
+
+let mkCompGenThenDoSequential m expr stmt = mkThenDoSequential DebugPointAtSequential.SuppressStmt m expr stmt
let rec mkSequentials spSeq g m es =
match es with
@@ -7575,6 +7647,33 @@ let rec MakeApplicationAndBetaReduceAux g (f, fty, tyargsl: TType list list, arg
let MakeApplicationAndBetaReduce g (f, fty, tyargsl, argl, m) =
MakeApplicationAndBetaReduceAux g (f, fty, tyargsl, argl, m)
+let (|NewDelegateExpr|_|) g expr =
+ match expr with
+ | Expr.Obj (lambdaId, ty, a, b, [TObjExprMethod(c, d, e, tmvs, body, f)], [], m) when isDelegateTy g ty ->
+ Some (lambdaId, tmvs, body, m, (fun bodyR -> Expr.Obj (lambdaId, ty, a, b, [TObjExprMethod(c, d, e, tmvs, bodyR, f)], [], m)))
+ | _ -> None
+
+let (|DelegateInvokeExpr|_|) g expr =
+ match expr with
+ | Expr.App ((Expr.Val (invokeRef, _, _)) as iref, fty, tyargs, (f :: args), m)
+ when invokeRef.LogicalName = "Invoke" && isFSharpDelegateTy g (tyOfExpr g f) ->
+ Some(iref, fty, tyargs, f, args, m)
+ | _ -> None
+
+let rec MakeFSharpDelegateInvokeAndTryBetaReduce g (invokeRef, f, fty, tyargs, argsl: Expr list, m) =
+ match f with
+ | Expr.Let (bind, body, mlet, _) ->
+ mkLetBind mlet bind (MakeFSharpDelegateInvokeAndTryBetaReduce g (invokeRef, body, fty, tyargs, argsl, m))
+ | _ ->
+ match f with
+ | NewDelegateExpr g (_, argvsl, body, m, _) when argvsl.Length = argsl.Length ->
+ let pairs, body = List.mapFoldBack (MultiLambdaToTupledLambdaIfNeeded g) (List.zip argvsl argsl) body
+ let argvs2, args2 = List.unzip (List.concat pairs)
+ mkLetsBind m (mkCompGenBinds argvs2 args2) body
+ | _ ->
+ // Remake the delegate invoke
+ Expr.App (invokeRef, fty, tyargs, (f :: argsl), m)
+
//---------------------------------------------------------------------------
// Adjust for expected usage
// Convert a use of a value to saturate to the given arity.
@@ -7973,10 +8072,10 @@ let LinearizeTopMatchAux g parent (spBind, m, tree, targets, m2, ty) =
| [] -> failwith "itemsProj: no items?"
| [_] -> x (* no projection needed *)
| tys -> Expr.Op (TOp.TupleFieldGet (tupInfoRef, i), tys, [x], m)
- let isThrowingTarget = function TTarget(_, x, _) -> isThrow x
+ let isThrowingTarget = function TTarget(_, x, _, _) -> isThrow x
if 1 + List.count isThrowingTarget targetsL = targetsL.Length then
// Have failing targets and ONE successful one, so linearize
- let (TTarget (vs, rhs, spTarget)) = List.find (isThrowingTarget >> not) targetsL
+ let (TTarget (vs, rhs, spTarget, _)) = List.find (isThrowingTarget >> not) targetsL
let fvs = vs |> List.map (fun v -> fst(mkLocal v.Range v.LogicalName v.Type)) (* fresh *)
let vtys = vs |> List.map (fun v -> v.Type)
let tmpTy = mkRefTupledVarsTy g vs
@@ -7984,12 +8083,12 @@ let LinearizeTopMatchAux g parent (spBind, m, tree, targets, m2, ty) =
AdjustValToTopVal tmp parent ValReprInfo.emptyValData
- let newTg = TTarget (fvs, mkRefTupledVars g m fvs, spTarget)
- let fixup (TTarget (tvs, tx, spTarget)) =
+ let newTg = TTarget (fvs, mkRefTupledVars g m fvs, spTarget, None)
+ let fixup (TTarget (tvs, tx, spTarget, flags)) =
match destThrow tx with
| Some (m, _, e) ->
let tx = mkThrow m tmpTy e
- TTarget(tvs, tx, spTarget) (* Throwing targets, recast it's "return type" *)
+ TTarget(tvs, tx, spTarget, flags) (* Throwing targets, recast it's "return type" *)
| None -> newTg (* Non-throwing target, replaced [new/old] *)
let targets = Array.map fixup targets
@@ -8356,7 +8455,7 @@ let mkIsInstConditional g m tgty vinpe v e2 e3 =
else
let mbuilder = new MatchBuilder(DebugPointAtBinding.NoneAtInvisible, m)
- let tg2 = TDSuccess([mkCallUnbox g m tgty vinpe], mbuilder.AddTarget(TTarget([v], e2, DebugPointForTarget.No)))
+ let tg2 = TDSuccess([mkCallUnbox g m tgty vinpe], mbuilder.AddTarget(TTarget([v], e2, DebugPointForTarget.No, None)))
let tg3 = mbuilder.AddResultTarget(e3, DebugPointForTarget.No)
let dtree = TDSwitch(vinpe, [TCase(DecisionTreeTest.IsInst(tyOfExpr g vinpe, tgty), tg2)], Some tg3, m)
let expr = mbuilder.Close(dtree, m, tyOfExpr g e2)
@@ -8708,8 +8807,8 @@ and RewriteDecisionTree env x =
let body = RewriteDecisionTree env body
TDBind (bind', body)
-and rewriteTarget env (TTarget(vs, e, spTarget)) =
- TTarget(vs, RewriteExpr env e, spTarget)
+and rewriteTarget env (TTarget(vs, e, spTarget, flags)) =
+ TTarget(vs, RewriteExpr env e, spTarget, flags)
and rewriteTargets env targets =
List.map (rewriteTarget env) (Array.toList targets)
@@ -8937,7 +9036,7 @@ let IsSimpleSyntacticConstantExpr g inputExpr =
and checkDecisionTreeCase vrefs (TCase(discrim, dtree)) =
(match discrim with DecisionTreeTest.Const _c -> true | _ -> false) && checkDecisionTree vrefs dtree
- and checkDecisionTreeTarget vrefs (TTarget(vs, e, _)) =
+ and checkDecisionTreeTarget vrefs (TTarget(vs, e, _, _)) =
let vrefs = ((vrefs, vs) ||> List.fold (fun s v -> s.Add v.Stamp))
checkExpr vrefs e
@@ -9299,12 +9398,31 @@ let mkUnitDelayLambda (g: TcGlobals) m e =
let uv, _ = mkCompGenLocal m "unitVar" g.unit_ty
mkLambda m uv (e, tyOfExpr g e)
+
let (|ValApp|_|) g vref expr =
match expr with
- // use 'seq { ... }' as an indicator
| Expr.App (Expr.Val (vref2, _, _), _f0ty, tyargs, args, m) when valRefEq g vref vref2 -> Some (tyargs, args, m)
| _ -> None
+let (|UseResumableStateMachinesExpr|_|) g expr =
+ match expr with
+ | ValApp g g.cgh__useResumableCode_vref (_, _, _m) -> Some ()
+ | _ -> None
+
+/// Match
+let (|IsThenElseExpr|_|) expr =
+ match expr with
+ | Expr.Match (_spBind, _exprm, TDSwitch(cond, [ TCase( DecisionTreeTest.Const (Const.Bool true), TDSuccess ([], 0) )], Some (TDSuccess ([], 1)), _),
+ [| TTarget([], thenExpr, _, _); TTarget([], elseExpr, _, _) |], _m, _ty) ->
+ Some (cond, thenExpr, elseExpr)
+ | _ -> None
+
+/// if __useResumableCode then ... else ...
+let (|IfUseResumableStateMachinesExpr|_|) g expr =
+ match expr with
+ | IsThenElseExpr(UseResumableStateMachinesExpr g (), thenExpr, elseExpr) -> Some (thenExpr, elseExpr)
+ | _ -> None
+
/// Combine a list of ModuleOrNamespaceType's making up the description of a CCU. checking there are now
/// duplicate modules etc.
let CombineCcuContentFragments m l =
@@ -9399,4 +9517,119 @@ let (|TryWithExpr|_|) expr =
Some (spTry, spWith, resTy, bodyExpr, filterVar, filterExpr, handlerVar, handlerExpr, m)
| _ -> None
+let (|MatchTwoCasesExpr|_|) expr =
+ match expr with
+ | Expr.Match (spBind, exprm, TDSwitch(cond, [ TCase( DecisionTreeTest.UnionCase (ucref, a), TDSuccess ([], tg1) )], Some (TDSuccess ([], tg2)), b), tgs, m, ty) ->
+
+ // How to rebuild this construct
+ let rebuild (cond, ucref, tg1, tg2, tgs) =
+ Expr.Match (spBind, exprm, TDSwitch(cond, [ TCase( DecisionTreeTest.UnionCase (ucref, a), TDSuccess ([], tg1) )], Some (TDSuccess ([], tg2)), b), tgs, m, ty)
+
+ Some (cond, ucref, tg1, tg2, tgs, rebuild)
+
+ | _ -> None
+
+/// match e with None -> ... | Some v -> ... or other variations of the same
+let (|MatchOptionExpr|_|) expr =
+ match expr with
+ | MatchTwoCasesExpr(cond, ucref, tg1, tg2, tgs, rebuildTwoCases) ->
+ let tgNone, tgSome = if ucref.CaseName = "None" then tg1, tg2 else tg2, tg1
+ match tgs.[tgNone], tgs.[tgSome] with
+ | TTarget([], noneBranchExpr, b1, b2),
+ TTarget([], Expr.Let(TBind(unionCaseVar, Expr.Op(TOp.UnionCaseProof a1, a2, a3, a4), a5),
+ Expr.Let(TBind(someVar, Expr.Op(TOp.UnionCaseFieldGet (a6a, a6b), a7, a8, a9), a10), someBranchExpr, a11, a12), a13, a14), a15, a16)
+ when unionCaseVar.LogicalName = "unionCase" ->
+
+ // How to rebuild this construct
+ let rebuild (cond, noneBranchExpr, someVar, someBranchExpr) =
+ let tgs = Array.zeroCreate 2
+ tgs.[tgNone] <- TTarget([], noneBranchExpr, b1, b2)
+ tgs.[tgSome] <- TTarget([], Expr.Let(TBind(unionCaseVar, Expr.Op(TOp.UnionCaseProof a1, a2, a3, a4), a5),
+ Expr.Let(TBind(someVar, Expr.Op(TOp.UnionCaseFieldGet (a6a, a6b), a7, a8, a9), a10), someBranchExpr, a11, a12), a13, a14), a15, a16)
+ rebuildTwoCases (cond, ucref, tg1, tg2, tgs)
+
+ Some (cond, noneBranchExpr, someVar, someBranchExpr, rebuild)
+ | _ -> None
+ | _ -> None
+
+let (|ResumableEntryAppExpr|_|) g expr =
+ match expr with
+ | ValApp g g.cgh__resumableEntry_vref (_, _, _m) -> Some ()
+ | _ -> None
+
+/// Match an (unoptimized) __resumableEntry expression
+let (|ResumableEntryMatchExpr|_|) g expr =
+ match expr with
+ | Expr.Let(TBind(matchVar, matchExpr, sp1), MatchOptionExpr (Expr.Val(matchVar2, b, c), noneBranchExpr, someVar, someBranchExpr, rebuildMatch), d, e) ->
+ match matchExpr with
+ | ResumableEntryAppExpr g () ->
+ if valRefEq g (mkLocalValRef matchVar) matchVar2 then
+
+ // How to rebuild this construct
+ let rebuild (noneBranchExpr, someBranchExpr) =
+ Expr.Let(TBind(matchVar, matchExpr, sp1), rebuildMatch (Expr.Val(matchVar2, b, c), noneBranchExpr, someVar, someBranchExpr), d, e)
+
+ Some (noneBranchExpr, someVar, someBranchExpr, rebuild)
+
+ else None
+
+ | _ -> None
+ | _ -> None
+
+let (|PossiblyCompiledTypeOfExpr|_|) g expr =
+ match expr with
+ | TypeOfExpr g ty -> Some ty
+ | Expr.Op(TOp.ILCall (_, _, _, _, _, _, _, ilMethRef, _, _, _), [],[Expr.Op (TOp.ILAsm ([ I_ldtoken (ILToken.ILType _) ], _), [ty], _, _)], _)
+ when ilMethRef.DeclaringTypeRef.Name = "System.Type" && ilMethRef.Name = "GetTypeFromHandle" ->
+ Some ty
+ | _ -> None
+
+let (|StructStateMachineExpr|_|) g expr =
+ match expr with
+ | ValApp g g.cgh__stateMachine_vref ([dataTy; _resultTy], [moveNext; setStateMachine; afterCode], _m) ->
+ match moveNext, setStateMachine, afterCode with
+ | NewDelegateExpr g (_, [[moveNextThisVar]], moveNextBody, _, _),
+ NewDelegateExpr g (_, [[setStateMachineThisVar;setStateMachineStateVar]], setStateMachineBody, _, _),
+ NewDelegateExpr g (_, [[afterCodeThisVar]], afterCodeBody, _, _) ->
+ Some (dataTy,
+ (moveNextThisVar, moveNextBody),
+ (setStateMachineThisVar, setStateMachineStateVar, setStateMachineBody),
+ (afterCodeThisVar, afterCodeBody))
+ | _ -> None
+ | _ -> None
+
+let (|ResumeAtExpr|_|) g expr =
+ match expr with
+ | ValApp g g.cgh__resumeAt_vref (_, [pcExpr], _m) -> Some pcExpr
+ | _ -> None
+
+// Detect sequencing constructs in state machine code
+let (|SequentialResumableCode|_|) (g: TcGlobals) expr =
+ match expr with
+
+ // e1; e2
+ | Expr.Sequential(e1, e2, NormalSeq, sp, m) ->
+ Some (e1, e2, m, (fun e1 e2 -> Expr.Sequential(e1, e2, NormalSeq, sp, m)))
+
+ // let __stack_step = e1 in e2
+ | Expr.Let(bind, e2, m, _) when bind.Var.CompiledName(g.CompilerGlobalState).StartsWith(stackVarPrefix) ->
+ Some (bind.Expr, e2, m, (fun e1 e2 -> mkLet bind.DebugPoint m bind.Var e1 e2))
+
+ | _ -> None
+
let mkLabelled m l e = mkCompGenSequential m (Expr.Op (TOp.Label l, [], [], m)) e
+
+let isResumableCodeTy g ty = ty |> stripTyEqns g |> (function TType_app(tcref, _) -> tyconRefEq g tcref g.ResumableCode_tcr | _ -> false)
+
+let rec isReturnsResumableCodeTy g ty =
+ if isFunTy g ty then isReturnsResumableCodeTy g (rangeOfFunTy g ty)
+ else isResumableCodeTy g ty
+
+let (|ResumableCodeInvoke|_|) g expr =
+ match expr with
+ // defn.Invoke x --> let arg = x in [defn][arg/x]
+ | Expr.App ((Expr.Val (invokeRef, _, _) as iref), a, b, (f :: args), m)
+ when invokeRef.LogicalName = "Invoke" && isReturnsResumableCodeTy g (tyOfExpr g f) ->
+ Some (iref, f, args, m, (fun (f2, args2) -> Expr.App ((iref, a, b, (f2 :: args2), m))))
+ | _ -> None
+
diff --git a/src/fsharp/TypedTreeOps.fsi b/src/fsharp/TypedTreeOps.fsi
index 8e3dc20b995..7120778d8a4 100755
--- a/src/fsharp/TypedTreeOps.fsi
+++ b/src/fsharp/TypedTreeOps.fsi
@@ -9,6 +9,7 @@ open Internal.Utilities.Collections
open Internal.Utilities.Rational
open FSharp.Compiler.AbstractIL
open FSharp.Compiler.AbstractIL.IL
+open FSharp.Compiler.CompilerGlobalState
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
open FSharp.Compiler.Xml
@@ -1309,8 +1310,13 @@ val MultiLambdaToTupledLambda: TcGlobals -> Val list -> Expr -> Val * Expr
val AdjustArityOfLambdaBody: TcGlobals -> int -> Val list -> Expr -> Val list * Expr
/// Make an application expression, doing beta reduction by introducing let-bindings
+/// if the function expression is a construction of a lambda
val MakeApplicationAndBetaReduce: TcGlobals -> Expr * TType * TypeInst list * Exprs * range -> Expr
+/// Make a delegate invoke expression for an F# delegate type, doing beta reduction by introducing let-bindings
+/// if the delegate expression is a construction of a delegate.
+val MakeFSharpDelegateInvokeAndTryBetaReduce: TcGlobals -> invokeRef: Expr * f: Expr * fty: TType * tyargs: TypeInst * argsl: Exprs * m: range -> Expr
+
/// Combine two static-resolution requirements on a type parameter
val JoinTyparStaticReq: TyparStaticReq -> TyparStaticReq -> TyparStaticReq
@@ -1542,6 +1548,9 @@ val isInterfaceTyconRef: TyconRef -> bool
/// Determine if a type is a delegate type
val isDelegateTy: TcGlobals -> TType -> bool
+/// Determine if a type is a delegate type defined in F#
+val isFSharpDelegateTy: TcGlobals -> TType -> bool
+
/// Determine if a type is an interface type
val isInterfaceTy: TcGlobals -> TType -> bool
@@ -1750,7 +1759,15 @@ val mkOptionDefaultValue: TcGlobals -> range -> TType -> Expr -> Expr -> Expr
val mkSequential: DebugPointAtSequential -> range -> Expr -> Expr -> Expr
-val mkCompGenSequential: range -> Expr -> Expr -> Expr
+val mkThenDoSequential: DebugPointAtSequential -> range -> expr: Expr -> stmt: Expr -> Expr
+
+/// This is used for tacking on code _before_ the expression. The SuppressStmt
+/// setting is used for debug points, suppressing the debug points for the statement if possible.
+val mkCompGenSequential: range -> stmt: Expr -> expr: Expr -> Expr
+
+/// This is used for tacking on code _after_ the expression. The SuppressStmt
+/// setting is used for debug points, suppressing the debug points for the statement if possible.
+val mkCompGenThenDoSequential: range -> expr: Expr -> stmt: Expr -> Expr
val mkSequentials: DebugPointAtSequential -> TcGlobals -> range -> Exprs -> Expr
@@ -2145,6 +2162,7 @@ val TryFindInternalsVisibleToAttr: ILAttribute -> string option
val IsMatchingSignatureDataVersionAttr: ILVersionInfo -> ILAttribute -> bool
val mkCompilationMappingAttr: TcGlobals -> int -> ILAttribute
+
val mkCompilationMappingAttrWithSeqNum: TcGlobals -> int -> int -> ILAttribute
@@ -2307,6 +2325,8 @@ type ExprRewritingEnv =
PreInterceptBinding: ((Expr -> Expr) -> Binding -> Binding option) option
IsUnderQuotations: bool }
+val RewriteDecisionTree: ExprRewritingEnv -> DecisionTree -> DecisionTree
+
val RewriteExpr: ExprRewritingEnv -> Expr -> Expr
val RewriteImplFile: ExprRewritingEnv -> TypedImplFile -> TypedImplFile
@@ -2424,8 +2444,32 @@ val EmptyTraitWitnessInfoHashMap: TcGlobals -> TraitWitnessInfoHashMap<'T>
/// Match expressions that are an application of a particular F# function value
val (|ValApp|_|): TcGlobals -> ValRef -> Expr -> (TypeInst * Exprs * range) option
+/// Match expressions that represent the creation of an instance of an F# delegate value
+val (|NewDelegateExpr|_|): TcGlobals -> Expr -> (Unique * Val list list * Expr * range * (Expr -> Expr)) option
+
+/// Match a .Invoke on a delegate
+val (|DelegateInvokeExpr|_|): TcGlobals -> Expr -> (Expr * TType * TypeInst * Expr * Exprs * range) option
+
+/// Match 'if __useResumableCode then ... else ...' expressions
+val (|IfUseResumableStateMachinesExpr|_|) : TcGlobals -> Expr -> (Expr * Expr) option
+
val CombineCcuContentFragments: range -> ModuleOrNamespaceType list -> ModuleOrNamespaceType
+/// Recognise a 'match __resumableEntry() with ...' expression
+val (|ResumableEntryMatchExpr|_|): g: TcGlobals -> Expr -> (Expr * Val * Expr * (Expr * Expr -> Expr)) option
+
+/// Recognise a '__stateMachine' expression
+val (|StructStateMachineExpr|_|):
+ g: TcGlobals ->
+ expr: Expr ->
+ (TType * (Val * Expr) * (Val * Val * Expr) * (Val * Expr)) option
+
+/// Recognise a sequential or binding construct in a resumable code
+val (|SequentialResumableCode|_|): g: TcGlobals -> Expr -> (Expr * Expr * range * (Expr -> Expr -> Expr)) option
+
+/// Recognise a '__resumeAt' expression
+val (|ResumeAtExpr|_|): g: TcGlobals -> Expr -> Expr option
+
/// Recognise a while expression
val (|WhileExpr|_|): Expr -> (DebugPointAtWhile * SpecialWhileLoopMarker * Expr * Expr * range) option
@@ -2441,6 +2485,12 @@ val (|TryFinallyExpr|_|): Expr -> (DebugPointAtTry * DebugPointAtFinally * TType
/// Add a label to use as the target for a goto
val mkLabelled: range -> ILCodeLabel -> Expr -> Expr
+/// Any delegate type with ResumableCode attribute, or any function returning such a delegate type
+val isResumableCodeTy: TcGlobals -> TType -> bool
+
+/// The delegate type ResumableCode, or any function returning this a delegate type
+val isReturnsResumableCodeTy: TcGlobals -> TType -> bool
+
/// Shared helper for binding attributes
val TryBindTyconRefAttribute:
g:TcGlobals ->
@@ -2451,3 +2501,9 @@ val TryBindTyconRefAttribute:
f2:(Attrib -> 'a option) ->
f3:(obj option list * (string * obj option) list -> 'a option)
-> 'a option
+
+val (|ResumableCodeInvoke|_|):
+ g:TcGlobals ->
+ expr: Expr ->
+ (Expr * Expr * Expr list * range * (Expr * Expr list -> Expr)) option
+
\ No newline at end of file
diff --git a/src/fsharp/TypedTreePickle.fs b/src/fsharp/TypedTreePickle.fs
index 0ed89d9e8bc..8b5d3be9248 100644
--- a/src/fsharp/TypedTreePickle.fs
+++ b/src/fsharp/TypedTreePickle.fs
@@ -1978,20 +1978,6 @@ and p_tycon_objmodel_kind x st =
| TTyconDelegate ss -> p_byte 3 st; p_slotsig ss st
| TTyconEnum -> p_byte 4 st
-and p_mustinline x st =
- p_byte (match x with
- | ValInline.PseudoVal -> 0
- | ValInline.Always -> 1
- | ValInline.Optional -> 2
- | ValInline.Never -> 3) st
-
-and p_basethis x st =
- p_byte (match x with
- | BaseVal -> 0
- | CtorThisVal -> 1
- | NormalVal -> 2
- | MemberThisVal -> 3) st
-
and p_vrefFlags x st =
match x with
| NormalValUse -> p_byte 0 st
@@ -2278,22 +2264,6 @@ and u_tycon_objmodel_kind st =
| 4 -> TTyconEnum
| _ -> ufailwith st "u_tycon_objmodel_kind"
-and u_mustinline st =
- match u_byte st with
- | 0 -> ValInline.PseudoVal
- | 1 -> ValInline.Always
- | 2 -> ValInline.Optional
- | 3 -> ValInline.Never
- | _ -> ufailwith st "u_mustinline"
-
-and u_basethis st =
- match u_byte st with
- | 0 -> BaseVal
- | 1 -> CtorThisVal
- | 2 -> NormalVal
- | 3 -> MemberThisVal
- | _ -> ufailwith st "u_basethis"
-
and u_vrefFlags st =
match u_byte st with
| 0 -> NormalValUse
@@ -2422,7 +2392,7 @@ and p_dtree_discrim x st =
| DecisionTreeTest.ActivePatternCase _ -> pfailwith st "DecisionTreeTest.ActivePatternCase: only used during pattern match compilation"
| DecisionTreeTest.Error _ -> pfailwith st "DecisionTreeTest.Error: only used during pattern match compilation"
-and p_target (TTarget(a, b, _)) st = p_tup2 p_Vals p_expr (a, b) st
+and p_target (TTarget(a, b, _, _)) st = p_tup2 p_Vals p_expr (a, b) st
and p_bind (TBind(a, b, _)) st = p_tup2 p_Val p_expr (a, b) st
and p_lval_op_kind x st =
@@ -2453,7 +2423,7 @@ and u_dtree_discrim st =
| 4 -> u_tup2 u_int u_ty st |> DecisionTreeTest.ArrayLength
| _ -> ufailwith st "u_dtree_discrim"
-and u_target st = let a, b = u_tup2 u_Vals u_expr st in (TTarget(a, b, DebugPointForTarget.No))
+and u_target st = let a, b = u_tup2 u_Vals u_expr st in (TTarget(a, b, DebugPointForTarget.No, None))
and u_bind st = let a = u_Val st in let b = u_expr st in TBind(a, b, DebugPointAtBinding.NoneAtSticky)
@@ -2598,7 +2568,7 @@ and p_expr expr st =
| Expr.LetRec (a, b, c, _) -> p_byte 7 st; p_tup3 p_binds p_expr p_dummy_range (a, b, c) st
| Expr.Let (a, b, c, _) -> p_byte 8 st; p_tup3 p_bind p_expr p_dummy_range (a, b, c) st
| Expr.Match (_, a, b, c, d, e) -> p_byte 9 st; p_tup5 p_dummy_range p_dtree p_targets p_dummy_range p_ty (a, b, c, d, e) st
- | Expr.Obj (_, b, c, d, e, f, g) -> p_byte 10 st; p_tup6 p_ty (p_option p_Val) p_expr p_methods p_intfs p_dummy_range (b, c, d, e, f, g) st
+ | Expr.Obj (_, b, c, d, e, f, g) -> p_byte 10 st; p_tup6 p_ty (p_option p_Val) p_expr p_methods p_intfs p_dummy_range (b, c, d, e, f, g) st
| Expr.StaticOptimization (a, b, c, d) -> p_byte 11 st; p_tup4 p_constraints p_expr p_expr p_dummy_range (a, b, c, d) st
| Expr.TyChoose (a, b, c) -> p_byte 12 st; p_tup3 p_tyar_specs p_expr p_dummy_range (a, b, c) st
| Expr.Quote (ast, _, _, m, ty) -> p_byte 13 st; p_tup3 p_expr p_dummy_range p_ty (ast, m, ty) st
@@ -2624,7 +2594,8 @@ and u_expr st =
let b = u_expr st
let c = u_int st
let d = u_dummy_range st
- Expr.Sequential (a, b, (match c with 0 -> NormalSeq | 1 -> ThenDoSeq | _ -> ufailwith st "specialSeqFlag"), DebugPointAtSequential.StmtOnly, d)
+ let dir = match c with 0 -> NormalSeq | 1 -> ThenDoSeq | _ -> ufailwith st "specialSeqFlag"
+ Expr.Sequential (a, b, dir, DebugPointAtSequential.SuppressStmt, d)
| 4 -> let a0 = u_option u_Val st
let b0 = u_option u_Val st
let b1 = u_Vals st
diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs
index 8642d6c56c9..1f9b6f9c615 100644
--- a/src/fsharp/fsi/fsi.fs
+++ b/src/fsharp/fsi/fsi.fs
@@ -2564,7 +2564,7 @@ type internal FsiInteractionProcessor
let expr = parseExpression tokenizer
let m = expr.Range
// Make this into "(); expr" to suppress generalization and compilation-as-function
- let exprWithSeq = SynExpr.Sequential (DebugPointAtSequential.ExprOnly, true, SynExpr.Const (SynConst.Unit,m.StartRange), expr, m)
+ let exprWithSeq = SynExpr.Sequential (DebugPointAtSequential.SuppressExpr, true, SynExpr.Const (SynConst.Unit,m.StartRange), expr, m)
mainThreadProcessParsedExpression ctok errorLogger (exprWithSeq, istate))
|> commitResult
diff --git a/src/fsharp/lib.fs b/src/fsharp/lib.fs
index ef4785546b3..837c0eec8be 100755
--- a/src/fsharp/lib.fs
+++ b/src/fsharp/lib.fs
@@ -237,6 +237,24 @@ let p23 (_x, y, _z) = y
let p33 (_x, _y, z) = z
+let p14 (x1, _x2, _x3, _x4) = x1
+
+let p24 (_x1, x2, _x3, _x4) = x2
+
+let p34 (_x1, _x2, x3, _x4) = x3
+
+let p44 (_x1, _x2, _x3, x4) = x4
+
+let p15 (x1, _x2, _x3, _x4, _x5) = x1
+
+let p25 (_x1, x2, _x3, _x4, _x5) = x2
+
+let p35 (_x1, _x2, x3, _x4, _x5) = x3
+
+let p45 (_x1, _x2, _x3, x4, _x5) = x4
+
+let p55 (_x1, _x2, _x3, _x4, x5) = x5
+
let map1Of2 f (a1, a2) = (f a1, a2)
let map2Of2 f (a1, a2) = (a1, f a2)
diff --git a/src/fsharp/lib.fsi b/src/fsharp/lib.fsi
index f7816c129b0..5cad69e4e81 100644
--- a/src/fsharp/lib.fsi
+++ b/src/fsharp/lib.fsi
@@ -160,6 +160,24 @@ val p23: _x:'a * y:'b * _z:'c -> 'b
val p33: _x:'a * _y:'b * z:'c -> 'c
+val p14: x1:'a * _x2:'b * _x3:'c * _x4:'d -> 'a
+
+val p24: _x1:'a * x2:'b * _x3:'c * _x4:'d -> 'b
+
+val p34: _x1:'a * _x2:'b * x3:'c * _x4:'d -> 'c
+
+val p44: _x1:'a * _x2:'b * _x3:'c * x4:'d -> 'd
+
+val p15: x1:'a * _x2:'b * _x3:'c * _x4:'d * _x5:'e -> 'a
+
+val p25: _x1:'a * x2:'b * _x3:'c * _x4:'d * _x5:'e -> 'b
+
+val p35: _x1:'a * _x2:'b * x3:'c * _x4:'d * _x5:'e -> 'c
+
+val p45: _x1:'a * _x2:'b * _x3:'c * x4:'d * _x5:'e -> 'd
+
+val p55: _x1:'a * _x2:'b * _x3:'c * _x4:'d * x5:'e -> 'e
+
val map1Of2: f:('a -> 'b) -> a1:'a * a2:'c -> 'b * 'c
val map2Of2: f:('a -> 'b) -> a1:'c * a2:'a -> 'c * 'b
diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy
index 334e6d7409c..f31b99b10c8 100644
--- a/src/fsharp/pars.fsy
+++ b/src/fsharp/pars.fsy
@@ -8,14 +8,14 @@ open System
open Internal.Utilities
open Internal.Utilities.Text.Parsing
+open Internal.Utilities.Library
+open Internal.Utilities.Library.Extras
open FSharp.Compiler
open FSharp.Compiler.AbstractIL
open FSharp.Compiler.AbstractIL
-open Internal.Utilities.Library
open FSharp.Compiler.ErrorLogger
open FSharp.Compiler.Features
-open Internal.Utilities.Library.Extras
open FSharp.Compiler.ParseHelpers
open FSharp.Compiler.Syntax
open FSharp.Compiler.Syntax.PrettyNaming
@@ -3354,7 +3354,7 @@ typedSeqExprEOF:
seqExpr:
| declExpr seps seqExpr
- { SynExpr.Sequential (DebugPointAtSequential.Both, true, $1, $3, unionRanges $1.Range $3.Range) }
+ { SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, true, $1, $3, unionRanges $1.Range $3.Range) }
| declExpr seps
{ $1 }
@@ -3363,10 +3363,10 @@ seqExpr:
{ $1 }
| declExpr THEN seqExpr %prec prec_then_before
- { SynExpr.Sequential (DebugPointAtSequential.Both, false, $1, $3, unionRanges $1.Range $3.Range ) }
+ { SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, false, $1, $3, unionRanges $1.Range $3.Range ) }
| declExpr OTHEN OBLOCKBEGIN typedSeqExpr oblockend %prec prec_then_before
- { SynExpr.Sequential (DebugPointAtSequential.Both, false, $1, $4, unionRanges $1.Range $4.Range) }
+ { SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, false, $1, $4, unionRanges $1.Range $4.Range) }
| hardwhiteLetBindings %prec prec_args_error
{ let hwlb, m = $1
diff --git a/src/fsharp/range.fs b/src/fsharp/range.fs
index 690420ba7e5..db51b5b8f45 100755
--- a/src/fsharp/range.fs
+++ b/src/fsharp/range.fs
@@ -58,6 +58,15 @@ type Position(code:int64) =
and pos = Position
+[]
+type RangeDebugPointKind =
+ | None
+ | While
+ | For
+ | Try
+ | Binding
+ | Finally
+
[]
module RangeImpl =
[]
@@ -78,6 +87,9 @@ module RangeImpl =
[]
let isSyntheticBitCount = 1
+ []
+ let debugPointKindBitCount = 3
+
[]
let fileIndexShift = 0
@@ -97,33 +109,40 @@ module RangeImpl =
let isSyntheticShift = 58
[]
- let fileIndexMask = 0b0000000000000000000000000000000000000000111111111111111111111111L
+ let debugPointKindShift = 59
+
+ []
+ let fileIndexMask = 0b0000000000000000000000000000000000000000111111111111111111111111L
[]
- let startColumnMask = 0b0000000000000000000011111111111111111111000000000000000000000000L
+ let startColumnMask = 0b0000000000000000000011111111111111111111000000000000000000000000L
[]
- let endColumnMask = 0b1111111111111111111100000000000000000000000000000000000000000000L
+ let endColumnMask = 0b1111111111111111111100000000000000000000000000000000000000000000L
[]
- let startLineMask = 0b0000000000000000000000000000000001111111111111111111111111111111L
+ let startLineMask = 0b0000000000000000000000000000000001111111111111111111111111111111L
[]
- let heightMask = 0b0000001111111111111111111111111110000000000000000000000000000000L
+ let heightMask = 0b0000001111111111111111111111111110000000000000000000000000000000L
[]
- let isSyntheticMask = 0b0000010000000000000000000000000000000000000000000000000000000000L
+ let isSyntheticMask = 0b0000010000000000000000000000000000000000000000000000000000000000L
+
+ []
+ let debugPointKindMask= 0b0011100000000000000000000000000000000000000000000000000000000000L
#if DEBUG
let _ = assert (posBitCount <= 64)
let _ = assert (fileIndexBitCount + startColumnBitCount + endColumnBitCount <= 64)
- let _ = assert (startLineBitCount + heightBitCount + isSyntheticBitCount <= 64)
+ let _ = assert (startLineBitCount + heightBitCount + isSyntheticBitCount + debugPointKindBitCount <= 64)
let _ = assert (startColumnShift = fileIndexShift + fileIndexBitCount)
let _ = assert (endColumnShift = startColumnShift + startColumnBitCount)
let _ = assert (heightShift = startLineShift + startLineBitCount)
let _ = assert (isSyntheticShift = heightShift + heightBitCount)
+ let _ = assert (debugPointKindShift = isSyntheticShift + isSyntheticBitCount)
let _ = assert (fileIndexMask = mask64 fileIndexShift fileIndexBitCount)
let _ = assert (startLineMask = mask64 startLineShift startLineBitCount)
@@ -131,6 +150,7 @@ module RangeImpl =
let _ = assert (heightMask = mask64 heightShift heightBitCount)
let _ = assert (endColumnMask = mask64 endColumnShift endColumnBitCount)
let _ = assert (isSyntheticMask = mask64 isSyntheticShift isSyntheticBitCount)
+ let _ = assert (debugPointKindMask = mask64 debugPointKindShift debugPointKindBitCount)
#endif
@@ -229,6 +249,15 @@ type Range(code1:int64, code2: int64) =
member r.IsSynthetic = int32((code2 &&& isSyntheticMask) >>> isSyntheticShift) <> 0
+ member r.DebugPointKind =
+ match int32((code2 &&& debugPointKindMask) >>> debugPointKindShift) with
+ | 1 -> RangeDebugPointKind.While
+ | 2 -> RangeDebugPointKind.For
+ | 3 -> RangeDebugPointKind.Try
+ | 4 -> RangeDebugPointKind.Finally
+ | 5 -> RangeDebugPointKind.Binding
+ | _ -> RangeDebugPointKind.None
+
member r.Start = pos (r.StartLine, r.StartColumn)
member r.End = pos (r.EndLine, r.EndColumn)
@@ -245,6 +274,17 @@ type Range(code1:int64, code2: int64) =
member r.MakeSynthetic() = range(code1, code2 ||| isSyntheticMask)
+ member r.NoteDebugPoint(kind) =
+ let code =
+ match kind with
+ | RangeDebugPointKind.None -> 0
+ | RangeDebugPointKind.While -> 1
+ | RangeDebugPointKind.For -> 2
+ | RangeDebugPointKind.Try -> 3
+ | RangeDebugPointKind.Finally -> 4
+ | RangeDebugPointKind.Binding -> 5
+ range(code1, code2 ||| (int64 code <<< debugPointKindShift))
+
member r.Code1 = code1
member r.Code2 = code2
@@ -273,7 +313,7 @@ type Range(code1:int64, code2: int64) =
override r.GetHashCode() = hash code1 + hash code2
- override r.ToString() = sprintf "%s (%d,%d--%d,%d) IsSynthetic=%b" r.FileName r.StartLine r.StartColumn r.EndLine r.EndColumn r.IsSynthetic
+ override r.ToString() = sprintf "%s (%d,%d--%d,%d)" r.FileName r.StartLine r.StartColumn r.EndLine r.EndColumn
and range = Range
@@ -341,6 +381,10 @@ module Range =
/// This is deliberately written in an allocation-free way, i.e. m1.Start, m1.End etc. are not called
let unionRanges (m1:range) (m2:range) =
if m1.FileIndex <> m2.FileIndex then m2 else
+
+ // If all identical then return m1. This preserves DebugPointKind when no merging takes place
+ if m1.Code1 = m2.Code1 && m1.Code2 = m2.Code2 then m1 else
+
let b =
if (m1.StartLine > m2.StartLine || (m1.StartLine = m2.StartLine && m1.StartColumn > m2.StartColumn)) then m2
else m1
diff --git a/src/fsharp/range.fsi b/src/fsharp/range.fsi
index 1d2fcdb8634..13d604afee4 100755
--- a/src/fsharp/range.fsi
+++ b/src/fsharp/range.fsi
@@ -8,6 +8,15 @@ open System.Collections.Generic
/// An index into a global tables of filenames
type internal FileIndex = int32
+[]
+type internal RangeDebugPointKind =
+ | None
+ | While
+ | For
+ | Try
+ | Binding
+ | Finally
+
/// Represents a position in a file
[]
type Position =
@@ -69,9 +78,16 @@ type Range =
/// service operations like dot-completion.
member IsSynthetic: bool
+ /// When de-sugaring computation expressions we convert a debug point into a plain range, and then later
+ /// recover that the range definitely indicates a debug point.
+ member internal DebugPointKind: RangeDebugPointKind
+
/// Convert a range to be synthetic
member internal MakeSynthetic: unit -> range
+ /// Note that a range indicates a debug point
+ member internal NoteDebugPoint: kind: RangeDebugPointKind -> range
+
/// Convert a range to string
member internal ToShortString: unit -> string
diff --git a/src/fsharp/service/FSharpParseFileResults.fs b/src/fsharp/service/FSharpParseFileResults.fs
index 7b2ecf82a3f..d4b1ab9cd7c 100644
--- a/src/fsharp/service/FSharpParseFileResults.fs
+++ b/src/fsharp/service/FSharpParseFileResults.fs
@@ -524,7 +524,9 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,
| SynInterpolatedStringPart.String _ -> ()
| SynInterpolatedStringPart.FillExpr (fillExpr, _) -> yield fillExpr ]
- | SynExpr.YieldOrReturn (_, e, _)
+ | SynExpr.YieldOrReturn (_, e, m) ->
+ yield! checkRange m
+ yield! walkExpr false e
| SynExpr.YieldOrReturnFrom (_, e, _)
| SynExpr.DoBang (e, _) ->
yield! checkRange e.Range
@@ -615,8 +617,8 @@ type FSharpParseFileResults(diagnostics: FSharpDiagnostic[], input: ParsedInput,
| SynExpr.SequentialOrImplicitYield (spSeq, e1, e2, _, _)
| SynExpr.Sequential (spSeq, _, e1, e2, _) ->
- yield! walkExpr (match spSeq with DebugPointAtSequential.ExprOnly -> false | _ -> true) e1
- yield! walkExpr (match spSeq with DebugPointAtSequential.StmtOnly -> false | _ -> true) e2
+ yield! walkExpr (match spSeq with DebugPointAtSequential.SuppressExpr | DebugPointAtSequential.SuppressBoth -> false | _ -> true) e1
+ yield! walkExpr (match spSeq with DebugPointAtSequential.SuppressStmt | DebugPointAtSequential.SuppressBoth -> false | _ -> true) e2
| SynExpr.IfThenElse (e1, e2, e3opt, spBind, _, _, _) ->
yield! walkBindSeqPt spBind
diff --git a/src/fsharp/symbols/Exprs.fs b/src/fsharp/symbols/Exprs.fs
index c1b456e0948..74ac59496a0 100644
--- a/src/fsharp/symbols/Exprs.fs
+++ b/src/fsharp/symbols/Exprs.fs
@@ -1186,7 +1186,7 @@ module FSharpExprConvert =
and ConvTargetsLinear cenv env tgs contF =
match tgs with
| [] -> contF []
- | TTarget(vars, rhs, _) :: rest ->
+ | TTarget(vars, rhs, _, _) :: rest ->
let varsR = (List.rev vars) |> List.map (ConvVal cenv)
ConvExprLinear cenv env rhs (fun targetR ->
ConvTargetsLinear cenv env rest (fun restR ->
diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs
index 0cf4f677a4f..8affc871220 100644
--- a/src/fsharp/symbols/Symbols.fs
+++ b/src/fsharp/symbols/Symbols.fs
@@ -1526,7 +1526,6 @@ type FSharpGenericParameterConstraint(cenv, cx: TyparConstraint) =
override x.ToString() = ""
type FSharpInlineAnnotation =
- | PseudoValue
| AlwaysInline
| OptionalInline
| NeverInline
@@ -1746,7 +1745,6 @@ type FSharpMemberOrFunctionOrValue(cenv, d:FSharpMemberOrValData, item) =
| None -> FSharpInlineAnnotation.OptionalInline
| Some v ->
match v.InlineInfo with
- | ValInline.PseudoVal -> FSharpInlineAnnotation.PseudoValue
| ValInline.Always -> FSharpInlineAnnotation.AlwaysInline
| ValInline.Optional -> FSharpInlineAnnotation.OptionalInline
| ValInline.Never -> FSharpInlineAnnotation.NeverInline
diff --git a/src/fsharp/symbols/Symbols.fsi b/src/fsharp/symbols/Symbols.fsi
index ba09617f18b..8cf040e5523 100644
--- a/src/fsharp/symbols/Symbols.fsi
+++ b/src/fsharp/symbols/Symbols.fsi
@@ -716,10 +716,7 @@ type FSharpGenericParameterConstraint =
[]
type FSharpInlineAnnotation =
- /// Indicates the value is inlined and compiled code for the function does not exist
- | PseudoValue
-
- /// Indicates the value is inlined but compiled code for the function still exists, e.g. to satisfy interfaces on objects, but that it is also always inlined
+ /// Indicates the value is always inlined in statically compiled code
| AlwaysInline
/// Indicates the value is optionally inlined
diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf
index 5f0d33d8905..d5847712cb2 100644
--- a/src/fsharp/xlf/FSComp.txt.cs.xlf
+++ b/src/fsharp/xlf/FSComp.txt.cs.xlf
@@ -177,6 +177,11 @@
uvolnění prázdných znaků
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patternvzor s jedním podtržítkem
@@ -237,6 +242,16 @@
Neplatná direktiva #{0} {1}
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Klíčové slovo, které specifikuje konstantní literál jako argument parametru typu v poskytovatelích typů
@@ -272,6 +287,11 @@
Hlavička zdroje začínající na posunu {0} má chybný formát.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesVytiskněte odvozená rozhraní všech kompilovaných souborů do přidružených souborů podpisu.
@@ -352,11 +372,66 @@
Algoritmus {0} není podporovaný.
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagersRegistrovaní správci PackageManagers nepodporují #i.
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Sadu .NET SDK pro tento skript nešlo určit. Pokud se skript nachází v adresáři používajícím global.json, zkontrolujte, jestli je nainstalovaná odpovídající sada .NET SDK. Výstup ze zadání {0} --version v adresáři {1} byl {2} a ukončovací kód byl {3}.
@@ -407,6 +482,11 @@
Typy Byref nejsou v deklaraci otevřeného typu povolené.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Neshoda v interpolovaném řetězci. Interpolované řetězce nemůžou používat specifikátory formátu %, pokud se každému z nich nezadá nějaký výraz, např. %d{{1+1}}.
@@ -417,6 +497,11 @@
Neplatné zarovnání v interpolovaném řetězci
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsPoužití [<Struct>] u hodnot, funkcí a metod je povolené jenom u částečných aktivních definic vzorů.
@@ -447,6 +532,36 @@
Konstrukt let! ... and! ... se dá použít jen v případě, že tvůrce výpočetních výrazů definuje buď metodu {0}, nebo vhodné metody MergeSource a Bind.
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Neplatný interpolovaný řetězec. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf
index 1e4d7679105..8857f03773b 100644
--- a/src/fsharp/xlf/FSComp.txt.de.xlf
+++ b/src/fsharp/xlf/FSComp.txt.de.xlf
@@ -177,6 +177,11 @@
Lockerung für Leerraum
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patternMuster mit einzelnem Unterstrich
@@ -237,6 +242,16 @@
Ungültige Direktive "#{0} {1}"
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Schlüsselwort, um ein konstantes Literal als Typparameterargument in Typanbietern anzugeben.
@@ -272,6 +287,11 @@
Der Ressourcenheader, der am Offset {0} beginnt, ist fehlerhaft formatiert.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesDrucken der abgeleiteten Schnittstellen aller Dateien an zugehörige Signaturdateien
@@ -352,11 +372,66 @@
Algorithmus "{0}" wird nicht unterstützt
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers#i wird von den registrierten PackageManagers nicht unterstützt.
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Das .NET SDK für dieses Skript konnte nicht ermittelt werden. Wenn sich das Skript in einem Verzeichnis mit "global.json" befindet, stellen Sie sicher, dass das entsprechende .NET SDK installiert ist. Ausgabe von "{0} --version" im Verzeichnis "{1}": {2}. Exitcode: {3}.
@@ -407,6 +482,11 @@
Byref-Typen sind in einer Deklaration für offene Typen nicht zulässig.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Konflikt in interpolierter Zeichenfolge. Interpolierte Zeichenfolgen dürfen keine Formatbezeichner vom Typ "%" verwenden, es sei denn, jeder erhält einen Ausdruck, z. B. "%d{{1+1}}"
@@ -417,6 +497,11 @@
Ungültige Ausrichtung in interpolierter Zeichenfolge.
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsDie Verwendung von "[<Struct>]" für Werte, Funktionen und Methoden ist nur für partielle aktive Musterdefinitionen zulässig.
@@ -447,6 +532,36 @@
Das Konstrukt "let! ... and! ..." kann nur verwendet werden, wenn der Berechnungsausdrucks-Generator entweder eine {0}-Methode oder geeignete MergeSource- und Bind-Methoden definiert.
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Ungültige interpolierte Zeichenfolge. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf
index cfcbe891641..3337d050643 100644
--- a/src/fsharp/xlf/FSComp.txt.es.xlf
+++ b/src/fsharp/xlf/FSComp.txt.es.xlf
@@ -177,6 +177,11 @@
relajación de espacio en blanco
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patternpatrón de subrayado simple
@@ -237,6 +242,16 @@
Directiva '#{0} {1}' no válida.
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Palabra clave para especificar un literal de constante como argumento de parámetro de tipo en los proveedores de tipo.
@@ -272,6 +287,11 @@
El encabezado de los recursos que comienza en el desplazamiento {0} está mal formado.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesImprimir las interfaces deducidas de todos los archivos de compilación en los archivos de signatura asociados
@@ -352,11 +372,66 @@
No se admite el algoritmo '{0}'
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagersLos elementos PackageManager registrados no admiten #i
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.No se pudo determinar el SDK de .NET para este script. Si el script está en un directorio que usa una instancia de "global.json", asegúrese de que el SDK de .NET pertinente esté instalado. La salida de "{0} --version" en el directorio "{1}" era "{2}", con el código de salida "{3}".
@@ -407,6 +482,11 @@
No se permiten tipos byref en una declaración de tipo abierto.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'La cadena interpolada no coincide. Las cadenas interpoladas no pueden usar los especificadores de formato "%", a menos que se les proporcione una expresión individualmente; por ejemplo, "%d{{1+1}}".
@@ -417,6 +497,11 @@
Alineación no válida en la cadena interpolada
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsEl uso de «[<Struct>]» en valores, funciones y métodos solo se permite en definiciones de modelos activos parciales.
@@ -447,6 +532,36 @@
La construcción "let! ... and! ..." solo se puede usar si el generador de expresiones de cálculo define un método "{0}" o bien los métodos "MergeSource" y "Bind" adecuados.
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Cadena interpolada no válida. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf
index f79eff950d1..9945871462e 100644
--- a/src/fsharp/xlf/FSComp.txt.fr.xlf
+++ b/src/fsharp/xlf/FSComp.txt.fr.xlf
@@ -177,6 +177,11 @@
assouplissement de la mise en retrait avec des espaces blancs
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patternmodèle de trait de soulignement unique
@@ -237,6 +242,16 @@
Directive non valide '#{0} {1}'
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Mot clé permettant de spécifier une constante littérale en tant qu'argument de paramètre de type dans les fournisseurs de types.
@@ -272,6 +287,11 @@
L'en-tête de ressource commençant au décalage {0} est mal formé.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesImprimer les interfaces inférées de tous les fichiers de compilation sur les fichiers de signature associés
@@ -352,11 +372,66 @@
Algorithme '{0}' non pris en charge
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers#i n'est pas pris en charge par les PackageManagers inscrits
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Le kit SDK .NET de ce script n'a pas pu être déterminé. Si le script se trouve dans un répertoire utilisant un fichier 'global.json', vérifiez que le kit SDK .NET approprié est installé. La sortie de '{0} --version' dans le répertoire '{1}' était '{2}', et le code de sortie était '{3}'.
@@ -407,6 +482,11 @@
Les types Byref ne sont pas autorisés dans une déclaration de type ouverte.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Incompatibilité dans la chaîne interpolée. Les chaînes interpolées ne peuvent pas utiliser les spécificateurs de format '%' à moins de recevoir une expression, par exemple '%d{{1+1}}'
@@ -417,6 +497,11 @@
Alignement non valide dans la chaîne interpolée
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsL’utilisation de' [<Struct>] 'sur les valeurs, les fonctions et les méthodes n’est autorisée que sur les définitions de modèle actif partiel
@@ -447,6 +532,36 @@
La construction 'let! ... and! ...' peut uniquement être utilisée si le générateur d'expressions de calcul définit une méthode '{0}' ou les méthodes 'MergeSource' et 'Bind' appropriées
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Chaîne interpolée non valide. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf
index fe47cbae173..dc045bd4020 100644
--- a/src/fsharp/xlf/FSComp.txt.it.xlf
+++ b/src/fsharp/xlf/FSComp.txt.it.xlf
@@ -177,6 +177,11 @@
uso meno restrittivo degli spazi vuoti
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patterncriterio per carattere di sottolineatura singolo
@@ -237,6 +242,16 @@
Direttiva '#{0} {1}' non valida
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Parola chiave per specificare un valore letterale di costante come argomento del parametro di tipo in Provider di tipi.
@@ -272,6 +287,11 @@
L'intestazione di risorsa che inizia a partire dall'offset {0} non è valida.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesStampare le interfacce derivate di tutti i file di compilazione nei file di firma associati
@@ -352,11 +372,66 @@
L'algoritmo '{0}' non è supportato
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers#i non è supportato dagli elementi PackageManager registrati
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Non è stato possibile determinare la versione di .NET SDK per questo script. Se lo script si trova in una directory che usa un file 'global.json', assicurarsi che sia installata la versione pertinente di .NET SDK. L'output di '{0} --version' nella directory '{1}' è '{2}' e il codice di uscita è '{3}'.
@@ -407,6 +482,11 @@
I tipi byref non sono consentiti in una dichiarazione di tipo aperto.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Mancata corrispondenza nella stringa interpolata. Nelle stringhe interpolate non è possibile usare gli identificatori di formato '%' a meno che non si indichi un'espressione per ognuno di essi, ad esempio '%d{{1+1}}'
@@ -417,6 +497,11 @@
Allineamento non valido nella stringa interpolata
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsL'utilizzo di '[<Struct>]' su valori, funzioni e metodi è consentito solo per definizioni di criteri attivi parziali
@@ -447,6 +532,36 @@
È possibile usare il costrutto 'let! ... and! ...' solo se il generatore di espressioni di calcolo definisce un metodo '{0}' o metodi 'MergeSource' e 'Bind' appropriati
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}La stringa interpolata non è valida. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf
index f61f49b12aa..8509d0f8df5 100644
--- a/src/fsharp/xlf/FSComp.txt.ja.xlf
+++ b/src/fsharp/xlf/FSComp.txt.ja.xlf
@@ -177,6 +177,11 @@
空白の緩和
+
+ resumable state machines
+ resumable state machines
+
+ single underscore pattern単一のアンダースコア パターン
@@ -237,6 +242,16 @@
無効なディレクティブ '#{0} {1}'
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.定数リテラルを型プロバイダーの型パラメーター引数として指定するキーワード。
@@ -272,6 +287,11 @@
オフセット {0} で始まるリソース ヘッダーの形式に誤りがあります。
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesすべてのコンパイル ファイルの推定されたインターフェイスを関連する署名ファイルに印刷します
@@ -357,6 +377,61 @@
登録された PackageManager によって、#i はサポートされていません
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.このスクリプトの .NET SDK を特定できませんでした。このスクリプトが、'global.json' が使用されているディレクトリにある場合は、関連する .NET SDK がインストールされていることを確認してください。ディレクトリ '{1}' の '{0} --version' からの出力は '{2}' で、終了コードは '{3}' でした。
@@ -407,6 +482,11 @@
Byref 型は、オープン型宣言では使用できません。
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'補間された文字列が一致しません。'%d{{1+1}}' などの式が指定されている場合を除き、補間された文字列では '%' 書式指定子を使用できません
@@ -417,6 +497,11 @@
補間された文字列内の配置が無効です
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitions値、関数、およびメソッドでの '[<Struct>]' は、部分的なアクティブ パターンの定義でのみ使うことができます
@@ -447,6 +532,36 @@
'let! ... and! ...' コンストラクトは、コンピュテーション式ビルダーが '{0}' メソッドまたは適切な 'MergeSource' および 'Bind' メソッドのいずれかを定義している場合にのみ使用できます
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}補間された文字列が無効です。{0}
diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf
index 98557a0a5c9..e5fb6dd8044 100644
--- a/src/fsharp/xlf/FSComp.txt.ko.xlf
+++ b/src/fsharp/xlf/FSComp.txt.ko.xlf
@@ -177,6 +177,11 @@
공백 완화
+
+ resumable state machines
+ resumable state machines
+
+ single underscore pattern단일 밑줄 패턴
@@ -237,6 +242,16 @@
잘못된 지시문 '#{0} {1}'
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.상수 리터럴을 형식 공급자의 형식 매개 변수 인수로 지정하는 키워드입니다.
@@ -272,6 +287,11 @@
오프셋 {0}에서 시작하는 리소스 헤더의 형식이 잘못되었습니다.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature files모든 컴파일 파일의 유추된 인터페이스를 관련 서명 파일로 인쇄합니다.
@@ -352,11 +372,66 @@
{0}' 알고리즘은 지원되지 않습니다.
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers#i는 등록된 PackageManagers에서 지원하지 않습니다.
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.이 스크립트에 대한 .NET SDK를 확인할 수 없습니다. 스크립트가 'global.json'을 사용하는 디렉터리에 있는 경우 관련 .NET SDK가 설치되어 있는지 확인하세요. '{1}' 디렉터리에 있는 '{0} --version'의 출력은 '{2}'이고 종료 코드는 '{3}'입니다.
@@ -407,6 +482,11 @@
Byref 형식은 개방형 형식 선언에서 허용되지 않습니다.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'보간 문자열의 불일치. 각 보간 문자열에 식(예: '%d{{1+1}}')이 지정되지 않는 한 '%' 형식 지정자를 사용할 수 없습니다.
@@ -417,6 +497,11 @@
보간 문자열의 잘못된 정렬
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitions값, 함수 및 메서드에 '[<Struct>]'을(를) 사용하는 것은 부분 활성 패턴 정의에서만 허용됩니다.
@@ -447,6 +532,36 @@
'let! ... and! ...' 구문은 계산 식 작성기에서 '{0}' 메서드 또는 적절한 'MergeSource' 및 'Bind' 메서드를 정의한 경우에만 사용할 수 있습니다.
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}잘못된 보간 문자열. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf
index 9c3f94da1bf..faa3780a31d 100644
--- a/src/fsharp/xlf/FSComp.txt.pl.xlf
+++ b/src/fsharp/xlf/FSComp.txt.pl.xlf
@@ -177,6 +177,11 @@
rozluźnianie reguł dotyczących odstępów
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patternwzorzec z pojedynczym podkreśleniem
@@ -237,6 +242,16 @@
Nieprawidłowa dyrektywa „#{0} {1}”
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Słowo kluczowe na potrzeby określania literału stałej jako argumentu parametru typu w przypadku dostawców typów.
@@ -272,6 +287,11 @@
Nagłówek zasobu rozpoczynający się od przesunięcia {0} jest nieprawidłowo sformułowany.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesDrukowanie wywnioskowanych interfejsów wszystkich plików kompilacji do skojarzonych plików sygnatur
@@ -352,11 +372,66 @@
Algorytm „{0}” nie jest obsługiwany
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagersElement #i nie jest obsługiwany przez zarejestrowanych menedżerów pakietów
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Nie można określić zestawu .NET SDK dla tego skryptu. Jeśli skrypt znajduje się w katalogu korzystającym z pliku „global.json”, upewnij się, że zainstalowano odpowiedni zestaw .NET SDK. Dane wyjściowe polecenia „{0} --version” w katalogu „{1}” to „{2}”, a kod zakończenia to „{3}”.
@@ -407,6 +482,11 @@
Typy ByRef są niedozwolone w deklaracji typu otwartego.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Niezgodność w interpolowanym ciągu. W interpolowanych ciągach nie można używać specyfikatorów formatu „%”, chyba że każdemu z nich odpowiada wyrażenie, na przykład „%d{{1+1}}”.
@@ -417,6 +497,11 @@
Nieprawidłowe wyrównanie w ciągu interpolowanym
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsUżywanie elementu "[<Struct>]" na wartościach, funkcjach i metodach jest dozwolone tylko w definicjach częściowo aktywnego wzorca
@@ -447,6 +532,36 @@
Konstrukcji „let! ... and! ...” można użyć tylko wtedy, gdy konstruktor wyrażeń obliczeniowych definiuje metodę „{0}” lub odpowiednie metody „MergeSource” i „Bind”
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Nieprawidłowy ciąg interpolowany. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf
index 171b10911ce..9e5a38bac97 100644
--- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf
+++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf
@@ -177,6 +177,11 @@
atenuação de espaço em branco
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patternpadrão de sublinhado simples
@@ -237,6 +242,16 @@
Diretriz inválida '#{0} {1}'
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Palavra-chave para especificar um literal constante como um argumento de parâmetro de tipo nos Provedores de Tipo.
@@ -272,6 +287,11 @@
O cabeçalho do recurso que começa no deslocamento {0} está malformado.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesImprimir as interfaces inferidas de todos os arquivos de compilação para os arquivos de assinatura associados
@@ -352,11 +372,66 @@
Algoritmo '{0}' sem suporte
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagersO PackageManagers registrado não dá suporte a #i
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Não foi possível determinar o SDK do .NET para esse script. Se o script estiver em um diretório usando um 'global.json', verifique se o SDK relevante do .NET está instalado. A saída de '{0} --version' no diretório '{1}' foi: '{2}' e o código de saída foi '{3}'.
@@ -407,6 +482,11 @@
Os tipos Byref não são permitidos em uma declaração de tipo aberto.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Incompatibilidade na cadeia de caracteres interpolada. As cadeias de caracteres interpoladas não podem usar especificadores de formato '%', a menos que cada um receba uma expressão, por exemplo, '%d{{1+1}}'
@@ -417,6 +497,11 @@
Alinhamento inválido na cadeia de caracteres interpolada
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsO uso de '[<Struct>]' em valores, funções e métodos somente é permitido em definições de padrões ativos parciais
@@ -447,6 +532,36 @@
O constructo 'let! ... and! ...' só pode ser usado se o construtor de expressões de computação definir um método '{0}' ou um método 'MergeSource' ou 'Bind' apropriado
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Cadeia de caracteres interpolada inválida. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf
index 07e804054e2..1fc4e95e281 100644
--- a/src/fsharp/xlf/FSComp.txt.ru.xlf
+++ b/src/fsharp/xlf/FSComp.txt.ru.xlf
@@ -177,6 +177,11 @@
уменьшение строгости для пробелов
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patternшаблон с одним подчеркиванием
@@ -237,6 +242,16 @@
Недопустимая директива "#{0} {1}"
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Ключевое слово для указания константного литерала в качестве аргумента параметра типа в поставщиках типов.
@@ -272,6 +287,11 @@
Заголовок ресурса некорректен начиная со смещения {0}.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesПечать определяемых интерфейсов всех файлов компиляции в связанные файлы подписей
@@ -352,11 +372,66 @@
Алгоритм "{0}" не поддерживается
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers#i не поддерживается зарегистрированными диспетчерами пакетов
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Не удалось определить пакет SDK .NET для этого сценария. Если сценарий находится в каталоге с использованием "global.json", убедитесь, что установлен соответствующий пакет SDK .NET. Выходные данные команды "{0} --version" в каталоге "{1}" — "{2}", а код выхода — "{3}".
@@ -407,6 +482,11 @@
Типы ByRef запрещены в объявлении открытого типа.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Несоответствие в интерполированной строке. В интерполированных строках запрещено использовать описатели формата "%", если только каждому из них не назначено выражение, например "'%d{{1+1}}".
@@ -417,6 +497,11 @@
Недопустимое выравнивание в интерполированной строке
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsИспользование "[<Struct>]" для значений, функций и методов разрешено только для определений частичных активных шаблонов
@@ -447,6 +532,36 @@
Конструкцию "let! ... and! ..." можно использовать только в том случае, если построитель выражений с вычислениями определяет либо метод "{0}", либо соответствующие методы "MergeSource" и "Bind"
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Недопустимая интерполированная строка. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf
index a87273642e1..3f2a42de36f 100644
--- a/src/fsharp/xlf/FSComp.txt.tr.xlf
+++ b/src/fsharp/xlf/FSComp.txt.tr.xlf
@@ -177,6 +177,11 @@
boşluk genişlemesi
+
+ resumable state machines
+ resumable state machines
+
+ single underscore patterntek alt çizgi deseni
@@ -237,6 +242,16 @@
Geçersiz yönerge '#{0} {1}'
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.Tür Sağlayıcılarında tür parametresi bağımsız değişkeni olarak sabit değişmez değeri belirtmek için anahtar sözcük.
@@ -272,6 +287,11 @@
{0} uzaklığında başlayan kaynak üst bilgisi hatalı biçimlendirilmiş.
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature filesTüm derleme dosyalarının çıkarsanan arabirimlerini ilişkili imza dosyalarına yazdır
@@ -352,11 +372,66 @@
{0}' algoritması desteklenmiyor
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers#i, kayıtlı PackageManagers tarafından desteklenmiyor
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.Bu betik için .NET SDK belirlenemedi. Betik 'global.json' kullanan bir dizindeyse, ilgili .NET SDK'nın yüklü olduğundan emin olun. '{1}' dizinindeki '{0} --version' çıkışı: '{2}' ve çıkış kodu: '{3}'.
@@ -407,6 +482,11 @@
Açık tür bildiriminde Byref türlerine izin verilmez.
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'Düz metin arasına kod eklenmiş dizede uyuşmazlık. Düz metin arasına kod eklenmiş dizeler, her birine '%d{{1+1}}' gibi bir ifade verilmedikçe '%' biçim belirticilerini kullanamaz
@@ -417,6 +497,11 @@
Düz metin arasına kod eklenmiş dizede geçersiz hizalama
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitionsDeğerlerde, işlevlerde ve yöntemlerde '[<Struct>]' kullanımına yalnızca kısmi etkin desen tanımlarında izin veriliyor
@@ -447,6 +532,36 @@
'let! ... and! ...' yapısı, yalnızca hesaplama ifadesi oluşturucu bir '{0}' metodunu ya da uygun 'MergeSource' ve 'Bind' metotlarını tanımlarsa kullanılabilir
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}Geçersiz düz metin arasına kod eklenmiş dize. {0}
diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf
index 6533177f43c..3970629c725 100644
--- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf
+++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf
@@ -177,6 +177,11 @@
空格松弛法
+
+ resumable state machines
+ resumable state machines
+
+ single underscore pattern单下划线模式
@@ -237,6 +242,16 @@
无效的指令“#{0} {1}”
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.用于将常量文本指定为类型提供程序中的类型形参实参的关键字。
@@ -272,6 +287,11 @@
以偏移量 {0} 开始的资源标头格式不正确。
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature files将所有编译文件的推断接口打印到关联的签名文件
@@ -352,11 +372,66 @@
不支持算法“{0}”
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers注册的 PackageManager 不支持 #i
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.无法确定此脚本的 .NET SDK。如果脚本在使用 "global.json" 的目录中,请确保已安装相关的 .NET SDK。目录“{1}”中 "{0} --version" 的输出为“{2}”,退出代码为“{3}”。
@@ -407,6 +482,11 @@
在开放类型声明中不允许使用 Byref 类型。
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'在内插字符串中不匹配。内插字符串不会使用 "%" 格式说明符,除非为每个字符串提供像 "'%d{{1+1}}" 这样的表达式
@@ -417,6 +497,11 @@
内插字符串中的对齐无效
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitions只允许在部分活动模式定义中对值、函数和方法使用 "[<Struct>]"
@@ -447,6 +532,36 @@
仅当计算表达式生成器定义了 "{0}" 方法或适当的 "MergeSource" 和 "Bind" 方法时,才可以使用 "let! ... and! ..." 构造
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}内插字符串无效。{0}
diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf
index 137fa6b4fd6..7f9913fec3d 100644
--- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf
+++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf
@@ -177,6 +177,11 @@
空白字元放寬
+
+ resumable state machines
+ resumable state machines
+
+ single underscore pattern單一底線模式
@@ -237,6 +242,16 @@
無效的指示詞 '#{0} {1}'
+
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+ The resumable code construct '{0}' may only be used in inlined code protected by 'if __useResumableCode then ...' and the overall composition must form valid resumable code.
+
+
+
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+ The 'InlineIfLambda' attribute is present in the signature but not the implementation.
+
+ Keyword to specify a constant literal as a type parameter argument in Type Providers.用於在型別提供者中,將常數常值指定為型別參數引數的關鍵字。
@@ -272,6 +287,11 @@
從位移 {0} 開始的資源標頭格式錯誤。
+
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+ The value '{0}' was marked 'InlineIfLambda' but was not determined to have a lambda value. This warning is for informational purposes only.
+
+ Print the inferred interfaces of all compilation files to associated signature files將所有編譯檔案的推斷介面列印至相關聯的簽章檔案
@@ -352,11 +372,66 @@
不支援演算法 '{0}'
+
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+ A target label for __resumeAt was not statically determined. A __resumeAt with a non-static target label may only appear at the start of a resumable code method
+
+
+
+ A fast integer for loop may not contain resumption points
+ A fast integer for loop may not contain resumption points
+
+
+
+ A 'let rec' occured in the resumable code specification
+ A 'let rec' occured in the resumable code specification
+
+
+
+ The 'with' block of a try/with may not contain resumption points
+ The 'with' block of a try/with may not contain resumption points
+
+
+
+ A try/finally may not contain resumption points
+ A try/finally may not contain resumption points
+
+
+
+ A delegate or function producing resumable code in a state machine has type parameters
+ A delegate or function producing resumable code in a state machine has type parameters
+
+
+
+ A resumable code invocation at '{0}' could not be reduced
+ A resumable code invocation at '{0}' could not be reduced
+
+
+
+ The resumable code value(s) '{0}' does not have a definition
+ The resumable code value(s) '{0}' does not have a definition
+
+ #i is not supported by the registered PackageManagers註冊的 PackageManagers 不支援 #i
+
+ The state machine has an unexpected form
+ The state machine has an unexpected form
+
+
+
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+ This state machine is not statically compilable. {0}. An alternative dynamic implementation will be used, which may be slower. Consider adjusting your code to ensure this state machine is statically compilable, or else suppress this warning.
+
+
+
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+ This state machine is not statically compilable and no alternative is available. {0}. Use an 'if __useResumableCode then <state-machine> else <alternative>' to give an alternative.
+
+ The .NET SDK for this script could not be determined. If the script is in a directory using a 'global.json' then ensure the relevant .NET SDK is installed. The output from '{0} --version' in the directory '{1}' was: '{2}' and the exit code was '{3}'.無法判斷這個指令碼的 .NET SDK。如果指令碼位於使用 'global.json' 的目錄中,請確認已安裝相關的 .NET SDK。目錄 '{1}' 中 '{0} --version' 的輸出為: '{2}',結束代碼為 '{3}'。
@@ -407,6 +482,11 @@
開放式類型宣告中不允許 Byref 類型。
+
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+ The 'InlineIfLambda' attribute may only be used on parameters of inlined functions of methods whose type is a function or F# delegate type.
+
+ Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'插補字串不相符。除非每個插補字串都有一個運算式,否則不可使用 '%' 格式指定名稱,例如 '%d{{1+1}}'
@@ -417,6 +497,11 @@
插補字串中的對齊無效
+
+ The construct '{0}' may only be used in valid resumable code.
+ The construct '{0}' may only be used in valid resumable code.
+
+ The use of '[<Struct>]' on values, functions and methods is only allowed on partial active pattern definitions只允許在部分現用模式定義上對值、函式和方法使用 '[<Struct>]'
@@ -447,6 +532,36 @@
只有在計算運算式產生器定義 '{0}' 方法或正確的 'MergeSource' 和 'Bind' 方法時,才可使用 'let! ... and! ...' 建構
+
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+ Invalid resumable code. A resumable code parameter must be of delegate or function type
+
+
+
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+ Invalid resumable code. Resumable code parameter must have name beginning with '__expand'
+
+
+
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+ Invalid resumable code. A 'let rec' occured in the resumable code specification
+
+
+
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+ Invalid resumable code. Any method of function accepting or returning resumable code must be marked 'inline'
+
+
+
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+ Resumable code invocation. Suppress this warning if you are defining new low-level resumable code in terms of existing resumable code.
+
+
+
+ Using resumable code or resumable state machines requires /langversion:preview
+ Using resumable code or resumable state machines requires /langversion:preview
+
+ Invalid interpolated string. {0}插補字串無效。{0}
diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
index e52d32d4ad5..5abb65bb7aa 100644
--- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
+++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
@@ -25,22 +25,16 @@ let run() = let x = 0 in foo(x,5)
.method public static void foo(int32 x,
!!a y) cil managed
{
-
- .maxstack 4
- .locals init (class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2 V_0,
- int32 V_1)
+
+ .maxstack 8
IL_0000: ldstr "%d"
IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5,class [runtime]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit,int32>::.ctor(string)
IL_000a: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatLine>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4)
- IL_000f: stloc.0
- IL_0010: ldarg.0
- IL_0011: stloc.1
- IL_0012: ldloc.0
- IL_0013: ldloc.1
- IL_0014: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2