diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index f7b7ab0489db1..adcf88ecc0450 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -31,3 +31,7 @@ How did we miss it? What tests are we adding to guard against it in the future?
**How was the bug found?**
(E.g. customer reported it vs. ad hoc testing)
+
+**Test documentation updated?**
+
+If this is a new non-compiler feature or a significant improvement to an existing feature, update https://github.com/dotnet/roslyn/wiki/Manual-Testing once you know which release it is targeting.
diff --git a/Compilers.sln b/Compilers.sln
index d60ca6d0d5602..6de7fab9a8500 100644
--- a/Compilers.sln
+++ b/Compilers.sln
@@ -93,8 +93,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RunTests", "src\Tools\Sourc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpCompilerTestUtilities.Desktop", "src\Compilers\Test\Utilities\CSharp.Desktop\CSharpCompilerTestUtilities.Desktop.csproj", "{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployDesktopTestRuntime", "src\Test\DeployDesktopTestRuntime\DeployDesktopTestRuntime.csproj", "{23683607-168A-4189-955E-908F0E80E60D}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "src\Test\Utilities\Portable\TestUtilities.csproj", "{CCBD3438-3E84-40A9-83AD-533F23BCFCA5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployCompilerGeneratorToolsRuntime", "src\Tools\Source\CompilerGeneratorTools\DeployCompilerGeneratorToolsRuntime\DeployCompilerGeneratorToolsRuntime.csproj", "{6DA08F12-32F2-4DD9-BBAD-982EB71A2C9B}"
@@ -331,10 +329,6 @@ Global
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}.Release|Any CPU.Build.0 = Release|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Release|Any CPU.Build.0 = Release|Any CPU
{CCBD3438-3E84-40A9-83AD-533F23BCFCA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCBD3438-3E84-40A9-83AD-533F23BCFCA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCBD3438-3E84-40A9-83AD-533F23BCFCA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -479,7 +473,6 @@ Global
{59BABFC3-C19B-4472-A93D-3DD3835BC219} = {6F016299-BA96-45BA-9BFF-6C0793979177}
{1A3941F1-1E1F-4EF7-8064-7729C4C2E2AA} = {6F016299-BA96-45BA-9BFF-6C0793979177}
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E} = {32A48625-F0AD-419D-828B-A50BDABA38EA}
- {23683607-168A-4189-955E-908F0E80E60D} = {6F016299-BA96-45BA-9BFF-6C0793979177}
{CCBD3438-3E84-40A9-83AD-533F23BCFCA5} = {6F016299-BA96-45BA-9BFF-6C0793979177}
{6DA08F12-32F2-4DD9-BBAD-982EB71A2C9B} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
{C1930979-C824-496B-A630-70F5369A636F} = {3CDEA9FB-CD44-4AB4-98A8-5537AAA2169B}
diff --git a/CrossPlatform.sln b/CrossPlatform.sln
index c5b076db9d040..4460a2cf7f40a 100644
--- a/CrossPlatform.sln
+++ b/CrossPlatform.sln
@@ -73,8 +73,6 @@ Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "BasicScripting", "src\Scrip
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpScriptingTest", "src\Scripting\CSharpTest\CSharpScriptingTest.csproj", "{2DAE4406-7A89-4B5F-95C3-BC5422CE47CE}"
EndProject
-Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "BasicScriptingTest", "src\Scripting\VisualBasicTest\BasicScriptingTest.vbproj", "{ABC7262E-1053-49F3-B846-E3091BB92E8C}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{A18BACE1-BB66-4156-8E89-81429A5814C6}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Debugging", "src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.shproj", "{D73ADF7D-2C1C-42AE-B2AB-EDC9497E4B71}"
@@ -242,10 +240,6 @@ Global
{2DAE4406-7A89-4B5F-95C3-BC5422CE47CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2DAE4406-7A89-4B5F-95C3-BC5422CE47CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2DAE4406-7A89-4B5F-95C3-BC5422CE47CE}.Release|Any CPU.Build.0 = Release|Any CPU
- {ABC7262E-1053-49F3-B846-E3091BB92E8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {ABC7262E-1053-49F3-B846-E3091BB92E8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {ABC7262E-1053-49F3-B846-E3091BB92E8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {ABC7262E-1053-49F3-B846-E3091BB92E8C}.Release|Any CPU.Build.0 = Release|Any CPU
{7AD4FE65-9A30-41A6-8004-AA8F89BCB7F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7AD4FE65-9A30-41A6-8004-AA8F89BCB7F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7AD4FE65-9A30-41A6-8004-AA8F89BCB7F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -341,7 +335,6 @@ Global
{066F0DBD-C46C-4C20-AFEC-99829A172625} = {A6F70573-57FE-49F9-A26C-75B8D202B795}
{3E7DEA65-317B-4F43-A25D-62F18D96CFD7} = {A6F70573-57FE-49F9-A26C-75B8D202B795}
{2DAE4406-7A89-4B5F-95C3-BC5422CE47CE} = {A6F70573-57FE-49F9-A26C-75B8D202B795}
- {ABC7262E-1053-49F3-B846-E3091BB92E8C} = {A6F70573-57FE-49F9-A26C-75B8D202B795}
{D73ADF7D-2C1C-42AE-B2AB-EDC9497E4B71} = {A18BACE1-BB66-4156-8E89-81429A5814C6}
{C1930979-C824-496B-A630-70F5369A636F} = {A18BACE1-BB66-4156-8E89-81429A5814C6}
{7AD4FE65-9A30-41A6-8004-AA8F89BCB7F3} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9}
diff --git a/Makefile b/Makefile
deleted file mode 100644
index c00e72729f700..0000000000000
--- a/Makefile
+++ /dev/null
@@ -1,69 +0,0 @@
-SHELL = /usr/bin/env bash
-OS_NAME = $(shell uname -s)
-BUILD_CONFIGURATION = Debug
-BINARIES_PATH = $(shell pwd)/Binaries
-SRC_PATH = $(shell pwd)/src
-BOOTSTRAP_PATH = $(BINARIES_PATH)/Bootstrap
-BUILD_LOG_PATH =
-HOME_DIR = $(shell cd ~ && pwd)
-DOTNET_VERSION = 1.0.1
-DOTNET = $(BINARIES_PATH)/dotnet-cli/dotnet
-TARGET_FX = netcoreapp1.1
-
-MSBUILD_ADDITIONALARGS := /v:m /fl /fileloggerparameters:Verbosity=normal /p:Configuration=$(BUILD_CONFIGURATION)
-
-ifeq ($(OS_NAME),Linux)
- RUNTIME_ID := $(shell . /etc/os-release && echo $$ID.$$VERSION_ID)-x64
-else ifeq ($(OS_NAME),Darwin)
- RUNTIME_ID := osx.10.12-x64
-endif
-
-ifneq ($(BUILD_LOG_PATH),)
- MSBUILD_ADDITIONALARGS := $(MSBUILD_ADDITIONALARGS) /fileloggerparameters:LogFile=$(BUILD_LOG_PATH)
-endif
-
-ifeq ($(BOOTSTRAP),true)
- MSBUILD_ARGS = $(MSBUILD_ADDITIONALARGS) /p:CscToolPath=$(BOOTSTRAP_PATH)/csc /p:CscToolExe=csc /p:VbcToolPath=$(BOOTSTRAP_PATH)/vbc /p:VbcToolExe=vbc
-else
- MSBUILD_ARGS = $(MSBUILD_ADDITIONALARGS)
-endif
-
-BUILD_CMD = dotnet build $(MSBUILD_ARGS)
-
-.PHONY: all bootstrap test toolset
-
-all: restore
- @export PATH="$(BINARIES_PATH)/dotnet-cli:$(PATH)" ; \
- export HOME="$(HOME_DIR)" ; \
- $(BUILD_CMD) CrossPlatform.sln
-
-bootstrap: restore
- export HOME="$(HOME_DIR)" ; \
- export PATH="$(BINARIES_PATH)/dotnet-cli:$(PATH)" ; \
- $(BUILD_CMD) src/Compilers/CSharp/CscCore && \
- $(BUILD_CMD) src/Compilers/VisualBasic/VbcCore && \
- mkdir -p $(BOOTSTRAP_PATH)/csc && mkdir -p $(BOOTSTRAP_PATH)/vbc && \
- dotnet publish -c $(BUILD_CONFIGURATION) -r $(RUNTIME_ID) src/Compilers/CSharp/CscCore -o $(BOOTSTRAP_PATH)/csc && \
- dotnet publish -c $(BUILD_CONFIGURATION) -r $(RUNTIME_ID) src/Compilers/VisualBasic/VbcCore -o $(BOOTSTRAP_PATH)/vbc
- rm -rf Binaries/$(BUILD_CONFIGURATION)
-
-test:
- @export PATH="$(BINARIES_PATH)/dotnet-cli:$(PATH)" ; \
- export HOME="$(HOME_DIR)" ; \
- dotnet publish -r $(RUNTIME_ID) src/Test/DeployCoreClrTestRuntime -o $(BINARIES_PATH)/$(BUILD_CONFIGURATION)/CoreClrTest -p:RoslynRuntimeIdentifier=$(RUNTIME_ID) && \
- build/scripts/tests.sh $(BUILD_CONFIGURATION)
-
-restore: $(DOTNET)
- export PATH="$(BINARIES_PATH)/dotnet-cli:$(PATH)" ; \
- ./build/scripts/restore.sh
-
-$(DOTNET):
- mkdir -p $(BINARIES_PATH) ; \
- pushd $(BINARIES_PATH) ; \
- curl -O https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0/scripts/obtain/dotnet-install.sh && \
- chmod +x dotnet-install.sh && \
- ./dotnet-install.sh --version "$(DOTNET_VERSION)" --install-dir "$(BINARIES_PATH)/dotnet-cli"
-
-
-clean:
- @rm -rf Binaries
diff --git a/README.md b/README.md
index b6b24fb2521d8..d59c465fc8863 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
|:--:|:--:|:--:|:--:|
|**master**|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_14_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_14_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_16_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_16_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/master/job/mac_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/master/job/mac_debug/)|
|**dev15.0.x**|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/linux_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/linux_debug/)||[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/mac_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/mac_debug/)|
-|**dev15.3.x**|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/linux_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/linux_debug/)||[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/mac_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/mac_debug/)|
+|**dev15.3.x**|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/ubuntu_14_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/ubuntu_14_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/ubuntu_16_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/ubuntu_16_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/mac_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.3.x/job/mac_debug/)|
|**dev16**|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/ubuntu_14_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/ubuntu_14_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/ubuntu_16_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/ubuntu_16_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/mac_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/mac_debug/)|
[//]: # (End current test results)
diff --git a/Roslyn.sln b/Roslyn.sln
index 1ddbfde0c1631..858d6f3b99c74 100644
--- a/Roslyn.sln
+++ b/Roslyn.sln
@@ -287,8 +287,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployCoreClrTestRuntime",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpCompilerTestUtilities.Desktop", "src\Compilers\Test\Utilities\CSharp.Desktop\CSharpCompilerTestUtilities.Desktop.csproj", "{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployDesktopTestRuntime", "src\Test\DeployDesktopTestRuntime\DeployDesktopTestRuntime.csproj", "{23683607-168A-4189-955E-908F0E80E60D}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessWatchdog", "src\Tools\ProcessWatchdog\ProcessWatchdog.csproj", "{1553DE60-A2B0-4FAF-B1B8-C0A7313781CC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeployCompilerGeneratorToolsRuntime", "src\Tools\Source\CompilerGeneratorTools\DeployCompilerGeneratorToolsRuntime\DeployCompilerGeneratorToolsRuntime.csproj", "{6DA08F12-32F2-4DD9-BBAD-982EB71A2C9B}"
@@ -835,10 +833,6 @@ Global
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E}.Release|Any CPU.Build.0 = Release|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {23683607-168A-4189-955E-908F0E80E60D}.Release|Any CPU.Build.0 = Release|Any CPU
{1553DE60-A2B0-4FAF-B1B8-C0A7313781CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1553DE60-A2B0-4FAF-B1B8-C0A7313781CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1553DE60-A2B0-4FAF-B1B8-C0A7313781CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1142,7 +1136,6 @@ Global
{43026D51-3083-4850-928D-07E1883D5B1A} = {3F40F71B-7DCF-44A1-B15C-38CA34824143}
{59BABFC3-C19B-4472-A93D-3DD3835BC219} = {CAD2965A-19AB-489F-BE2E-7649957F914A}
{7A4B2176-7BFD-4B75-A61A-E25A1FDD0A1E} = {32A48625-F0AD-419D-828B-A50BDABA38EA}
- {23683607-168A-4189-955E-908F0E80E60D} = {CAD2965A-19AB-489F-BE2E-7649957F914A}
{1553DE60-A2B0-4FAF-B1B8-C0A7313781CC} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
{6DA08F12-32F2-4DD9-BBAD-982EB71A2C9B} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
{A88AB44F-7F9D-43F6-A127-83BB65E5A7E2} = {CC126D03-7EAC-493F-B187-DCDEE1EF6A70}
diff --git a/build/Defaults/Net460/app.config b/build/Defaults/Net460/app.config
index 8db45c0c5c11b..5a4e96c5d40a8 100644
--- a/build/Defaults/Net460/app.config
+++ b/build/Defaults/Net460/app.config
@@ -11,7 +11,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/Targets/Imports.targets b/build/Targets/Imports.targets
index e023fabe73946..0bc26aacab180 100644
--- a/build/Targets/Imports.targets
+++ b/build/Targets/Imports.targets
@@ -3,34 +3,13 @@
-
+
<_IsAnyUnitTest>true
true$(OutputPath)UnitTests\$(MSBuildProjectName)\
-
-
-
- <_CopyReferences>$(DeveloperBuild)
- <_CopyProjectReferences>$(DeveloperBuild)
- <_IsAnyUnitTest>true
- <_IsAnyPortableUnitTest>true
- false
- $(OutputPath)Dlls\$(MSBuildProjectName)\
-
-
<_CopyReferences>false
@@ -38,15 +17,6 @@
$(OutputPath)Vsix\$(MSBuildProjectName)\
-
-
-
- $(AllowedReferenceRelatedFileExtensions);.pdb
- true
- $(OutputPath)$(RoslynUnitTestDir)
-
-
@@ -100,7 +70,7 @@
true
- false
+ falsefalse
+ false
+
$(MSBuildSDKsPath)\Microsoft.NET.Sdk\Sdk\PackageReference$(LiveUnitTestingOriginalBaseIntermediateOutputPath)project.assets.json
+
+
+ CurrentRuntime
@@ -103,6 +112,12 @@
portable
+
+
+ netcoreapp2.0
diff --git a/build/ToolsetPackages/BaseToolset.csproj b/build/ToolsetPackages/BaseToolset.csproj
index d5e60f94eebb2..9ba6a3b5e89d8 100644
--- a/build/ToolsetPackages/BaseToolset.csproj
+++ b/build/ToolsetPackages/BaseToolset.csproj
@@ -17,6 +17,7 @@
+
@@ -26,6 +27,5 @@
-
diff --git a/build/config/SignToolData.json b/build/config/SignToolData.json
index 4cf776083649d..a79c0812b76a5 100644
--- a/build/config/SignToolData.json
+++ b/build/config/SignToolData.json
@@ -102,13 +102,12 @@
"Vsix\\VisualStudioSetup.Next\\Roslyn.VisualStudio.Setup.Next.vsix",
"Vsix\\VisualStudioSetup\\Roslyn.VisualStudio.Setup.vsix",
"Vsix\\Roslyn\\RoslynDeployment.vsix",
- "Vsix\\Templates\\Roslyn Templates\\Release\\Roslyn SDK.vsix"
+ "Vsix\\Templates\\Roslyn SDK.vsix"
]
}
],
"exclude": [
- "Esent.Interop.dll",
"Microsoft.CodeAnalysis.Elfie.dll",
"Microsoft.DiaSymReader.dll",
"Microsoft.DiaSymReader.Native.amd64.dll",
diff --git a/build/scripts/build-utils.ps1 b/build/scripts/build-utils.ps1
index c0882b07769a3..1e6da820560e9 100644
--- a/build/scripts/build-utils.ps1
+++ b/build/scripts/build-utils.ps1
@@ -215,10 +215,10 @@ function Get-PackageDir([string]$name, [string]$version = "") {
# The intent of this script is to locate and return the path to the MSBuild directory that
# we should use for bulid operations. The preference order for MSBuild to use is as
-# follows
+# follows:
#
# 1. MSBuild from an active VS command prompt
-# 2. MSBuild from a machine wide VS install
+# 2. MSBuild from a compatible VS installation
# 3. MSBuild from the xcopy toolset
#
# This function will return two values: the kind of MSBuild chosen and the MSBuild directory.
@@ -232,9 +232,6 @@ function Get-MSBuildKindAndDir([switch]$xcopy = $false) {
# MSBuild from an active VS command prompt.
if (${env:VSINSTALLDIR} -ne $null) {
-
- # This line deliberately avoids using -ErrorAction. Inside a VS command prompt
- # an MSBuild command should always be available.
$command = (Get-Command msbuild -ErrorAction SilentlyContinue)
if ($command -ne $null) {
$p = Split-Path -parent $command.Path
@@ -278,14 +275,29 @@ function Get-MSBuildDir([switch]$xcopy = $false) {
# meets our minimal requirements for the Roslyn repo.
function Get-VisualStudioDirAndId() {
$vswhere = Join-Path (Ensure-BasicTool "vswhere") "tools\vswhere.exe"
- $output = & $vswhere -requires Microsoft.Component.MSBuild -format json | Out-String
- if (-not $?) {
- throw "Could not locate a valid Visual Studio"
+ $output = Exec-Command $vswhere "-requires Microsoft.Component.MSBuild -format json" | Out-String
+ $j = ConvertFrom-Json $output
+ foreach ($obj in $j) {
+
+ # Need to be using at least Visual Studio 15.2 in order to have the appropriate
+ # set of SDK fixes. Parsing the installationName is the only place where this is
+ # recorded in that form.
+ $name = $obj.installationName
+ if ($name -match "VisualStudio(Preview)?/([\d.]+)(\+|-).*") {
+ $minVersion = New-Object System.Version "15.1.0"
+ $version = New-Object System.Version $matches[2]
+ if ($version -ge $minVersion) {
+ Write-Output $obj.installationPath
+ Write-Output $obj.instanceId
+ return
+ }
+ }
+ else {
+ Write-Host "Unrecognized installationName format $name"
+ }
}
- $j = ConvertFrom-Json $output
- Write-Output $j[0].installationPath
- Write-Output $j[0].instanceId
+ throw "Could not find a suitable Visual Studio Version"
}
# Get the directory of the first Visual Studio which meets our minimal
diff --git a/build/scripts/build.ps1 b/build/scripts/build.ps1
index d48acef745789..22446e4356752 100644
--- a/build/scripts/build.ps1
+++ b/build/scripts/build.ps1
@@ -344,6 +344,13 @@ function Test-XUnit() {
$args += " -trait:Feature=NetCore"
}
+ # Exclude out the multi-targetted netcore app projects
+ $dlls = $dlls | ?{ -not ($_.FullName -match ".*netcoreapp.*") }
+
+ # Exclude out the ref assemblies
+ $dlls = $dlls | ?{ -not ($_.FullName -match ".*\\ref\\.*") }
+ $dlls = $dlls | ?{ -not ($_.FullName -match ".*/ref/.*") }
+
if ($cibuild) {
# Use a 50 minute timeout on CI
$args += " -xml -timeout:50"
@@ -445,7 +452,7 @@ function Ensure-ProcDump() {
Remove-Item -Re $filePath -ErrorAction SilentlyContinue
Create-Directory $outDir
$zipFilePath = Join-Path $toolsDir "procdump.zip"
- Invoke-WebRequest "https://download.sysinternals.com/files/Procdump.zip" -outfile $zipFilePath | Out-Null
+ Invoke-WebRequest "https://download.sysinternals.com/files/Procdump.zip" -UseBasicParsing -outfile $zipFilePath | Out-Null
Add-Type -AssemblyName System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::ExtractToDirectory($zipFilePath, $outDir)
}
diff --git a/build/scripts/obtain_dotnet.sh b/build/scripts/obtain_dotnet.sh
new file mode 100644
index 0000000000000..7410d0811171f
--- /dev/null
+++ b/build/scripts/obtain_dotnet.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Copyright (c) .NET Foundation and contributors. All rights reserved.
+# Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+# Source this script to ensure dotnet is installed and on the path.
+# If the FORCE_DOWNLOAD environment variable is set to "true", the system's dotnet install is ignored,
+# and dotnet is downloaded and installed locally.
+
+set -e
+set -u
+
+# check if `dotnet` is already on the PATH
+if command -v dotnet >/dev/null 2>&1
+then
+ if [[ "${FORCE_DOWNLOAD:-false}" != true ]]
+ then
+ exit 0
+ fi
+fi
+
+# This is a function to keep variable assignments out of the parent script (that is sourcing this file)
+install_dotnet () {
+ # Download and install `dotnet` locally
+ local DOTNET_VERSION=1.0.1
+ local THIS_DIR=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+ local DOTNET_PATH=${THIS_DIR}/../../Binaries/dotnet-cli
+
+ if [[ ! -x "${DOTNET_PATH}/dotnet" ]]
+ then
+ echo "Downloading and installing .NET CLI version ${DOTNET_VERSION} to ${DOTNET_PATH}"
+ curl https://raw.githubusercontent.com/dotnet/cli/rel/${DOTNET_VERSION}/scripts/obtain/dotnet-install.sh | \
+ /usr/bin/env bash -s -- --version "${DOTNET_VERSION}" --install-dir "${DOTNET_PATH}"
+ else
+ echo "Skipping download of .NET CLI: Already installed at ${DOTNET_PATH}"
+ fi
+
+ export PATH=${DOTNET_PATH}:${PATH}
+}
+install_dotnet
diff --git a/build/scripts/run_perf.ps1 b/build/scripts/run_perf.ps1
index bcd50bfd47765..4aa558a7e7f40 100644
--- a/build/scripts/run_perf.ps1
+++ b/build/scripts/run_perf.ps1
@@ -12,11 +12,11 @@ if ( -not $? )
exit 1
}
-Invoke-WebRequest -Uri http://dotnetci.blob.core.windows.net/roslyn-perf/cpc.zip -OutFile cpc.zip
+Invoke-WebRequest -Uri http://dotnetci.blob.core.windows.net/roslyn-perf/cpc.zip -OutFile cpc.zip -UseBasicParsing
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null
[IO.Compression.ZipFile]::ExtractToDirectory('cpc.zip', $CPCLocation)
-./build/scripts/cibuild.ps1 -release -testPerfRun
+./build/scripts/cibuild.cmd -release -testPerfRun
if ( -not $? )
{
diff --git a/build/scripts/tests.sh b/build/scripts/tests.sh
index 2db9437ca5f4c..51b2a8d00ff1b 100755
--- a/build/scripts/tests.sh
+++ b/build/scripts/tests.sh
@@ -1,20 +1,29 @@
#!/usr/bin/env bash
+# Copyright (c) .NET Foundation and contributors. All rights reserved.
+# Licensed under the MIT license. See LICENSE file in the project root for full license information.
-BUILD_CONFIGURATION=$1
+set -e
+set -u
-# This function will update the PATH variable to put the desired
-# version of Mono ahead of the system one.
+BUILD_CONFIGURATION=${1:-Debug}
-cd Binaries/$BUILD_CONFIGURATION/CoreClrTest
+THIS_DIR=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+BINARIES_PATH=${THIS_DIR}/../../Binaries
+SRC_PATH=${THIS_DIR}/../../src
+TEST_DIR=${BINARIES_PATH}/${BUILD_CONFIGURATION}/CoreClrTest
-chmod +x ./corerun
+RUNTIME_ID=$(dotnet --info | awk '/RID:/{print $2;}')
+
+BUILD_ARGS="-c ${BUILD_CONFIGURATION} -r ${RUNTIME_ID} /consoleloggerparameters:Verbosity=minimal;summary /p:RoslynRuntimeIdentifier=${RUNTIME_ID}"
+dotnet publish ${SRC_PATH}/Test/DeployCoreClrTestRuntime -o ${TEST_DIR} ${BUILD_ARGS}
+
+cd ${TEST_DIR}
mkdir -p xUnitResults
-./corerun ./xunit.console.netcore.exe *.UnitTests.dll -parallel all -xml xUnitResults/TestResults.xml
+dotnet exec ./xunit.console.netcore.exe *.UnitTests.dll -parallel all -xml xUnitResults/TestResults.xml
if [ $? -ne 0 ]; then
echo Unit test failed
exit 1
fi
-
diff --git a/cibuild.sh b/cibuild.sh
index 7781ddb5f0c9f..fa600d76797f0 100755
--- a/cibuild.sh
+++ b/cibuild.sh
@@ -1,5 +1,9 @@
#!/usr/bin/env bash
+# Copyright (c) .NET Foundation and contributors. All rights reserved.
+# Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
set -e
+set -u
usage()
{
@@ -9,21 +13,29 @@ usage()
echo "Options"
echo " --debug Build Debug (default)"
echo " --release Build Release"
+ echo " --cleanrun Clean the project before building"
echo " --skiptest Do not run tests"
- echo " --skipcrossgen Do not crossgen the bootstrapped compiler"
echo " --skipcommitprinting Do not print commit information"
- echo " --nocache Force download of toolsets"
}
+THIS_DIR=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+BINARIES_PATH=${THIS_DIR}/Binaries
+BOOTSTRAP_PATH=${BINARIES_PATH}/Bootstrap
+SRC_PATH=${THIS_DIR}/src
+BUILD_LOG_PATH=${BINARIES_PATH}/Build.log
+
BUILD_CONFIGURATION=Debug
-USE_CACHE=true
+CLEAN_RUN=false
SKIP_TESTS=false
-SKIP_CROSSGEN=false
SKIP_COMMIT_PRINTING=false
-MAKE="make"
-if [[ $OSTYPE == *[Bb][Ss][Dd]* ]]; then
- MAKE="gmake"
+# $HOME is unset when running the mac unit tests.
+if [[ -z ${HOME+x} ]]
+then
+ # Note that while ~ usually refers to $HOME, in the case where $HOME is unset,
+ # it looks up the current user's home dir, which is what we want.
+ # https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html
+ export HOME=$(cd ~ && pwd)
fi
# LTTNG is the logging infrastructure used by coreclr. Need this variable set
@@ -51,18 +63,14 @@ do
BUILD_CONFIGURATION=Release
shift 1
;;
- --nocache)
- USE_CACHE=false
+ --cleanrun)
+ CLEAN_RUN=true
shift 1
;;
--skiptests)
SKIP_TESTS=true
shift 1
;;
- --skipcrossgen)
- SKIP_CROSSGEN=true
- shift 1
- ;;
--skipcommitprinting)
SKIP_COMMIT_PRINTING=true
shift 1
@@ -74,8 +82,6 @@ do
esac
done
-MAKE_ARGS="BUILD_CONFIGURATION=$BUILD_CONFIGURATION SKIP_CROSSGEN=$SKIP_CROSSGEN"
-
if [ "$CLEAN_RUN" == "true" ]; then
echo Clean out the enlistment
git clean -dxf .
@@ -86,13 +92,33 @@ if [ "$SKIP_COMMIT_PRINTING" == "false" ]; then
git show --no-patch --pretty=raw HEAD
fi
-echo Building Bootstrap
-$MAKE bootstrap $MAKE_ARGS
+# obtain_dotnet.sh puts the right dotnet on the PATH
+FORCE_DOWNLOAD=true
+source ${THIS_DIR}/build/scripts/obtain_dotnet.sh
-echo Building CrossPlatform.sln
-$MAKE all $MAKE_ARGS BOOTSTRAP=true BUILD_LOG_PATH=Binaries/Build.log
+RUNTIME_ID=$(dotnet --info | awk '/RID:/{print $2;}')
+echo "Using Runtime Identifier: ${RUNTIME_ID}"
-if [ "$SKIP_TESTS" == "false" ]; then
- $MAKE test $MAKE_ARGS
-fi
+RESTORE_ARGS="-r ${RUNTIME_ID} -v Minimal --disable-parallel"
+echo "Restoring BaseToolset.csproj"
+dotnet restore ${RESTORE_ARGS} ${THIS_DIR}/build/ToolsetPackages/BaseToolset.csproj
+echo "Restoring CrossPlatform.sln"
+dotnet restore ${RESTORE_ARGS} ${THIS_DIR}/CrossPlatform.sln
+
+BUILD_ARGS="-c ${BUILD_CONFIGURATION} -r ${RUNTIME_ID} /nologo /consoleloggerparameters:Verbosity=minimal;summary /filelogger /fileloggerparameters:Verbosity=normal;logFile=${BUILD_LOG_PATH} /p:RoslynRuntimeIdentifier=${RUNTIME_ID} /maxcpucount:1"
+
+echo "Building bootstrap CscCore"
+dotnet publish ${SRC_PATH}/Compilers/CSharp/CscCore -o ${BOOTSTRAP_PATH}/csc ${BUILD_ARGS}
+echo "Building bootstrap VbcCore"
+dotnet publish ${SRC_PATH}/Compilers/VisualBasic/VbcCore -o ${BOOTSTRAP_PATH}/vbc ${BUILD_ARGS}
+rm -rf ${BINARIES_PATH}/${BUILD_CONFIGURATION}
+BUILD_ARGS+=" /p:CscToolPath=${BOOTSTRAP_PATH}/csc /p:CscToolExe=csc /p:VbcToolPath=${BOOTSTRAP_PATH}/vbc /p:VbcToolExe=vbc"
+echo "Building CrossPlatform.sln"
+dotnet build ${THIS_DIR}/CrossPlatform.sln ${BUILD_ARGS}
+
+if [[ "${SKIP_TESTS}" == false ]]
+then
+ echo "Running tests"
+ ${THIS_DIR}/build/scripts/tests.sh ${BUILD_CONFIGURATION}
+fi
diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md
index 013c0757e2413..2e5c99b30368b 100644
--- a/docs/Language Feature Status.md
+++ b/docs/Language Feature Status.md
@@ -22,6 +22,7 @@ efforts behind them.
| ------- | ------ | ----- | ---------- | -------- | --------- |
| [ref readonly](https://github.com/dotnet/csharplang/blob/master/proposals/readonly-ref.md) | [readonly-ref](https://github.com/dotnet/roslyn/tree/features/readonly-ref) | Prototype | [vsadov](https://github.com/vsadov), [omar](https://github.com/OmarTawfikw) | [cston](https://github.com/cston) | [jaredpar](https://github.com/jaredpar) |
| [blittable](https://github.com/dotnet/csharplang/pull/206) | None | Proposal | None | | [jaredpar](https://github.com/jaredpar) |
+| strongname | [strongname](https://github.com/dotnet/roslyn/tree/features/strongname) | In Progress | [Ty Overby](https://github.com/tyoverby) | | [jaredpar](https://github.com/jaredpar) |
| [interior pointer](https://github.com/dotnet/csharplang/pull/264) | None | Proposal | [vsadov](https://github.com/vsadov) | [jaredpar](https://github.com/jaredpar) | [jaredpar](https://github.com/jaredpar) |
| [non-trailing named arguments](https://github.com/dotnet/csharplang/blob/master/proposals/non-trailing-named-arguments.md) | [non-trailing](https://github.com/dotnet/roslyn/tree/features/non-trailing) | Prototype | [jcouv](https://github.com/jcouv) | TBD | [jcouv](https://github.com/jcouv) |
diff --git a/docs/compilers/Deterministic Inputs.md b/docs/compilers/Deterministic Inputs.md
index b43e9971f2b8b..2c47ccf10a7aa 100644
--- a/docs/compilers/Deterministic Inputs.md
+++ b/docs/compilers/Deterministic Inputs.md
@@ -5,7 +5,7 @@ We are aiming to make the compilers ultimately deterministic (https://github.com
The following are considered inputs to the compiler for the purpose of determinism:
-- The sequence of command-line flags
+- The sequence of command-line parameters
- The contents of the compiler's `.rsp` response file.
- The precise version of the compiler used, and its referenced assemblies
- Current full directory path (you can reduce this to a relative path; see https://github.com/dotnet/roslyn/issues/949)
diff --git a/docs/features/deconstruction.md b/docs/features/deconstruction.md
index 59543ee9a9c8b..5da7d3045a3b0 100644
--- a/docs/features/deconstruction.md
+++ b/docs/features/deconstruction.md
@@ -188,7 +188,7 @@ foreach_variable_statement // new
**References**
-[C# Design NOtes for Oct 25-26, 2016](https://github.com/dotnet/csharplang/blob/master/meetings/2016/LDM-2016-10-25-26.md)
+[C# Design Notes for Oct 25-26, 2016](https://github.com/dotnet/csharplang/blob/master/meetings/2016/LDM-2016-10-25-26.md)
[C# Design Notes for Sep 6, 2016](https://github.com/dotnet/csharplang/blob/master/meetings/2016/LDM-2016-09-06.md)
diff --git a/docs/infrastructure/cross-platform.md b/docs/infrastructure/cross-platform.md
index 9d5be313efd0a..c24fbff412a1b 100644
--- a/docs/infrastructure/cross-platform.md
+++ b/docs/infrastructure/cross-platform.md
@@ -4,26 +4,15 @@
Linux and Mac support for developing Roslyn is very much a work in progress. Not everything is supported at the moment and the steps detailed on this page will change very frequently. If this is an area you are interested in then please check back frequently for updates.
-## Building using a pre-made toolset
+## Building
-Right now Roslyn builds on *nix using a mix of Mono and CoreCLR. Patching the right Mono version and acquiring all the tools
-can be very difficult, so we've saved pre-built versions on Azure.
+Build all cross-platform projects with: `dotnet build CrossPlatform.sln`.
-Running `make` should download all these toolset binaries and kick off a build using MSBuild running on Mono.
+If you do not have a system-wide `dotnet` install, you can obtain one with `./build/scripts/obtain_dotnet.sh`. This will install a compatible version of the CLI to `./Binaries/dotnet-cli` - add this to your PATH before trying to build `CrossPlatform.sln`. Alternatively, sourcing the script with `source ./build/scripts/obtain_dotnet.sh` will add it to your PATH for you.
## Using the compiler
-After building there should be at least two versions of `csc.exe` in your output directory.
-
-The first is in the `Binaries/Debug` directory. This is the "full .NET framework" version. That means it expects to run on a
-full .NET framework, like either the Windows .NET framework or Mono. You would run this like you run other mono programs, i.e.
-`mono csc.exe`.
-
-The second copy is in the `Binaries/Debug/csccore` directory. This is a version running directly on CoreCLR -- no Mono necessary.
-Just run `csc` in that directory. Note that this version includes a copy of CoreCLR in the output directory, so it is not portable.
-The version of CoreCLR copied is specific to whatever machine you built with, so if you're running OS X, this will only run on OS X.
-Similarly with Linux (and whatever distro you're using).
-
+After building, there will be a `csc` in the `Binaries/Debug/Exes/CscCore` directory.
### Known issues when running `csc.exe`
diff --git a/docs/infrastructure/unix-toolset.md b/docs/infrastructure/unix-toolset.md
deleted file mode 100644
index 7946c32097366..0000000000000
--- a/docs/infrastructure/unix-toolset.md
+++ /dev/null
@@ -1,29 +0,0 @@
-Building a new Unix toolset
-====
-This document describes building a new toolset for use in Mac or Linux.
-Because the toolsets contain various targets and reference assemblies that
-only exist on Windows, the toolsets currently must be built on Windows.
-
-### Building Roslyn Toolset
-The new *toolset name* will be chosen as one of the follownig:
-
-- Linux: roslyn.linux.``
-- Mac: roslyn.mac.``
-
-The value of *version number* will simply be the one number higher than the current version number of the toolset.
-
-To build the toolset do the following:
-
-- If necessary, make modifications to the dependencies in the
- `build/MSBuildToolset/project.json` file to bring in anything new.
-- Run the `build/MSBuildToolset/build-toolset.ps1` file.
-- The script produces two zip files in bin\Debug\netcoreapp1.0 subdirectory:
- - Rename `osx.10.10-x64.zip` to roslyn.mac.`.zip`
- - Rename `ubuntu.14.04-x64.zip` to roslyn.linux.`.zip`
-- Upload the files to the Azure in the dotnetci storage account in the roslyn container:
-
-```
-azcopy /Pattern:*.zip /Source:build\MSBuildToolset\bin\Debug\netcoreapp1.0 /Dest:https://dotnetci.blob.core.windows.net/roslyn /DestKey:<>
-```
-
-- Send a PR to change [Makefile](https://github.com/dotnet/roslyn/blob/master/Makefile) to use the new toolset.
diff --git a/netci.groovy b/netci.groovy
index 6be39abf538ab..e69bbaf1373dc 100644
--- a/netci.groovy
+++ b/netci.groovy
@@ -103,7 +103,7 @@ commitPullList.each { isPr ->
def myJob = job(jobName) {
description("Ubuntu 14.04 tests")
steps {
- shell("./cibuild.sh --nocache --debug")
+ shell("./cibuild.sh --debug")
}
}
@@ -120,7 +120,7 @@ commitPullList.each { isPr ->
def myJob = job(jobName) {
description("Ubuntu 16.04 tests")
steps {
- shell("./cibuild.sh --nocache --debug")
+ shell("./cibuild.sh --debug")
}
}
@@ -137,7 +137,7 @@ commitPullList.each { isPr ->
def myJob = job(jobName) {
description("Mac tests")
steps {
- shell("./cibuild.sh --nocache --debug")
+ shell("./cibuild.sh --debug")
}
}
@@ -226,7 +226,7 @@ commitPullList.each { isPr ->
def triggerPhraseOnly = false
def triggerPhraseExtra = ""
- Utilities.setMachineAffinity(myJob, 'Windows_NT', 'latest-dev15-3-preview2')
+ Utilities.setMachineAffinity(myJob, 'Windows_NT', 'latest-dev15-3')
Utilities.addXUnitDotNETResults(myJob, '**/xUnitResults/*.xml')
addRoslynJob(myJob, jobName, branchName, isPr, triggerPhraseExtra, triggerPhraseOnly)
}
diff --git a/src/CodeStyle/CSharp/Tests/CSharpCodeStyleTests.csproj b/src/CodeStyle/CSharp/Tests/CSharpCodeStyleTests.csproj
index 6f3d9b1addd9c..60982a8a45da7 100644
--- a/src/CodeStyle/CSharp/Tests/CSharpCodeStyleTests.csproj
+++ b/src/CodeStyle/CSharp/Tests/CSharpCodeStyleTests.csproj
@@ -9,7 +9,7 @@
LibraryMicrosoft.CodeAnalysis.CSharpMicrosoft.CodeAnalysis.CSharp.CodeStyle.UnitTests
- netstandard1.3
+ $(RoslynPortableTargetFrameworks)UnitTestPortable
@@ -26,7 +26,8 @@
+
-
\ No newline at end of file
+
diff --git a/src/CodeStyle/Core/Tests/CodeStyleTests.csproj b/src/CodeStyle/Core/Tests/CodeStyleTests.csproj
index 1a61ff8516a17..c73eb02dacf43 100644
--- a/src/CodeStyle/Core/Tests/CodeStyleTests.csproj
+++ b/src/CodeStyle/Core/Tests/CodeStyleTests.csproj
@@ -9,7 +9,7 @@
LibraryMicrosoft.CodeAnalysisMicrosoft.CodeAnalysis.CodeStyle.UnitTests
- netstandard1.3
+ $(RoslynPortableTargetFrameworks)UnitTestPortable
@@ -26,7 +26,8 @@
+
-
\ No newline at end of file
+
diff --git a/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj b/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj
index 161b94cf4d4e1..0402a439ca485 100644
--- a/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj
+++ b/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj
@@ -8,7 +8,7 @@
AnyCPULibraryMicrosoft.CodeAnalysis.VisualBasic.CodeStyle.UnitTests
- netstandard1.3
+ $(RoslynPortableTargetFrameworks)UnitTestPortable
@@ -25,7 +25,8 @@
+
-
\ No newline at end of file
+
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
index 452a3ff705893..cdc16abb3213a 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
@@ -22,7 +22,7 @@ internal partial class Binder
// For example, if were binding attributes on delegate type symbol for below code snippet:
// [A1]
// [return: A2]
- // public delegate void Foo();
+ // public delegate void Goo();
// attributesToBind will only contain first attribute syntax.
internal static void BindAttributeTypes(ImmutableArray binders, ImmutableArray attributesToBind, Symbol ownerSymbol, NamedTypeSymbol[] boundAttributeTypes, DiagnosticBag diagnostics)
{
@@ -521,9 +521,9 @@ protected virtual MethodSymbol BindAttributeConstructor(
/// if named constructor arguments are used.
///
/// For example:
- /// void Foo(int x, int y, int z, int w = 3);
+ /// void Goo(int x, int y, int z, int w = 3);
///
- /// Foo(0, z: 2, y: 1);
+ /// Goo(0, z: 2, y: 1);
///
/// Arguments returned: 0, 1, 2, 3
///
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs
index 258a7b503a75f..73090c5e04c99 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs
@@ -358,11 +358,11 @@ private ImmutableArray ComputeSortedCrefMembers(NamespaceOrTypeSymbol co
// to not introduce a regression and breaking change we return NULL in this case.
// e.g.
//
- // ///
- // class Foo { }
+ // ///
+ // class Goo { }
//
// This cref used not to bind to anything, because before it was looking for a type and
- // since there was no arity, it didn't find Foo. Now however, it finds Foo.ctor,
+ // since there was no arity, it didn't find Goo. Now however, it finds Goo.ctor,
// which is arguably correct, but would be a breaking change (albeit with minimal impact)
// so we catch this case and chuck out the symbol found.
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
index 3659a3b0304b6..2a74d572e59fd 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
@@ -3629,7 +3629,7 @@ private BoundExpression BindDelegateCreationExpression(ObjectCreationExpressionS
// 4. A delegate type.
else if (argument.Type.TypeKind == TypeKind.Delegate)
{
- var sourceDelegate = argument.Type as NamedTypeSymbol;
+ var sourceDelegate = (NamedTypeSymbol)argument.Type;
MethodGroup methodGroup = MethodGroup.GetInstance();
try
{
@@ -5200,8 +5200,8 @@ private BoundExpression BindMemberAccessWithBoundLeft(
{
// There are some sources of a `dynamic` typed value that can be known before runtime
// to be invalid. For example, accessing a set-only property whose type is dynamic:
- // dynamic Foo { set; }
- // If Foo itself is a dynamic thing (e.g. in `x.Foo.Bar`, `x` is dynamic, and we're
+ // dynamic Goo { set; }
+ // If Goo itself is a dynamic thing (e.g. in `x.Goo.Bar`, `x` is dynamic, and we're
// currently checking Bar), then CheckValue will do nothing.
boundLeft = CheckValue(boundLeft, BindValueKind.RValue, diagnostics);
return BindDynamicMemberAccess(node, boundLeft, right, invoked, indexed, diagnostics);
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
index bcca00ac96a6e..9492de04d8f01 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
@@ -579,7 +579,7 @@ private BoundExpression BindMethodGroupInvocation(
{
if (HasApplicableConditionalMethod(resolution.OverloadResolutionResult))
{
- // warning CS1974: The dynamically dispatched call to method 'Foo' may fail at runtime
+ // warning CS1974: The dynamically dispatched call to method 'Goo' may fail at runtime
// because one or more applicable overloads are conditional methods
Error(diagnostics, ErrorCode.WRN_DynamicDispatchToConditionalMethod, syntax, methodGroup.Name);
}
@@ -627,15 +627,14 @@ private BoundExpression BindLocalFunctionInvocationWithDynamicArgument(
CSharpSyntaxNode queryClause,
MethodGroupResolution resolution)
{
- // Invocations of local functions with dynamic arguments
- // don't need to be dispatched as dynamic invocations
- // since they cannot be overloaded. Instead, we'll just
- // emit a standard call with dynamic implicit conversions
- // for any dynamic arguments. The one exception is params
- // arguments which cannot be targeted by dynamic arguments
- // because there is an ambiguity between an array target
- // and the params element target. See
- // https://github.com/dotnet/roslyn/issues/10708
+ // Invocations of local functions with dynamic arguments don't need
+ // to be dispatched as dynamic invocations since they cannot be
+ // overloaded. Instead, we'll just emit a standard call with
+ // dynamic implicit conversions for any dynamic arguments. There
+ // are two exceptions: "params", and unconstructed generics. While
+ // implementing those cases with dynamic invocations is possible,
+ // we have decided the implementation complexity is not worth it.
+ // Refer to the comments below for the exact semantics.
Debug.Assert(resolution.IsLocalFunctionInvocation);
Debug.Assert(resolution.OverloadResolutionResult.Succeeded);
@@ -650,9 +649,10 @@ private BoundExpression BindLocalFunctionInvocationWithDynamicArgument(
var methodResult = validResult.Result;
// We're only in trouble if a dynamic argument is passed to the
- // params parameter and is ambiguous at compile time between
- // normal and expanded form i.e., there is exactly one dynamic
- // argument to a params parameter
+ // params parameter and is ambiguous at compile time between normal
+ // and expanded form i.e., there is exactly one dynamic argument to
+ // a params parameter
+ // See https://github.com/dotnet/roslyn/issues/10708
if (OverloadResolution.IsValidParams(localFunction) &&
methodResult.Kind == MemberResolutionKind.ApplicableInNormalForm)
{
@@ -682,6 +682,31 @@ private BoundExpression BindLocalFunctionInvocationWithDynamicArgument(
}
}
+ // If we call an unconstructed generic local function with a
+ // dynamic argument in a place where it influences the type
+ // parameters, we need to dynamically dispatch the call (as the
+ // function must be constructed at runtime). We cannot do that, so
+ // disallow that. However, doing a specific analysis of each
+ // argument and its corresponding parameter to check if it's
+ // generic (and allow dynamic in non-generic parameters) may break
+ // overload resolution in the future, if we ever allow overloaded
+ // local functions. So, just disallow any mixing of dynamic and
+ // inferred generics. (Explicit generic arguments are fine)
+ // See https://github.com/dotnet/roslyn/issues/21317
+ if (boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty && localFunction.IsGenericMethod)
+ {
+ Error(diagnostics,
+ ErrorCode.ERR_DynamicLocalFunctionTypeParameter,
+ syntax, localFunction.Name);
+ return BindDynamicInvocation(
+ syntax,
+ boundMethodGroup,
+ resolution.AnalyzedArguments,
+ resolution.OverloadResolutionResult.GetAllApplicableMembers(),
+ diagnostics,
+ queryClause);
+ }
+
return BindInvocationExpressionContinued(
node: syntax,
expression: expression,
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
index eaa7cddcfeea0..569cea3042685 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
@@ -298,7 +298,7 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Diag
if (state.clauses.IsEmpty() && state.selectOrGroup.Kind() == SyntaxKind.SelectClause)
{
- var select = state.selectOrGroup as SelectClauseSyntax;
+ var select = (SelectClauseSyntax)state.selectOrGroup;
BoundCall invocation;
if (join.Into == null)
{
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs b/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs
index c572d31ea1645..e35c683f11bcf 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs
@@ -133,7 +133,7 @@ private static bool IsJoinRangeVariableInLeftKey(SimpleNameSyntax node)
{
if (parent.Kind() == SyntaxKind.JoinClause)
{
- var join = parent as JoinClauseSyntax;
+ var join = (JoinClauseSyntax)parent;
if (join.LeftExpression.Span.Contains(node.Span) && join.Identifier.ValueText == node.Identifier.ValueText) return true;
}
}
@@ -149,7 +149,7 @@ private static bool IsInJoinRightKey(SimpleNameSyntax node)
{
if (parent.Kind() == SyntaxKind.JoinClause)
{
- var join = parent as JoinClauseSyntax;
+ var join = (JoinClauseSyntax)parent;
if (join.RightExpression.Span.Contains(node.Span)) return true;
}
}
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
index 3fa46c8bc37fb..ee10352f9f93e 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
@@ -964,7 +964,10 @@ protected BoundLocalDeclaration BindVariableDeclaration(
foreach (var diagnostic in constantValueDiagnostics)
{
diagnostics.Add(diagnostic);
- hasErrors = true;
+ if (diagnostic.Severity == DiagnosticSeverity.Error)
+ {
+ hasErrors = true;
+ }
}
}
@@ -2527,8 +2530,8 @@ internal void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics
// It is possible that the conversion from lambda to delegate is just fine, and
// that we ended up here because the target type, though itself is not an error
// type, contains a type argument which is an error type. For example, converting
- // (Foo foo)=>{} to Action is a perfectly legal conversion even if Foo is undefined!
- // In that case we have already reported an error that Foo is undefined, so just bail out.
+ // (Goo goo)=>{} to Action is a perfectly legal conversion even if Goo is undefined!
+ // In that case we have already reported an error that Goo is undefined, so just bail out.
if (reason == LambdaConversionResult.Success)
{
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
index 0369beb373040..e11ce26fe0e88 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
@@ -599,7 +599,7 @@ protected Symbol BindNonGenericSimpleNamespaceOrTypeOrAliasSymbol(
{
var identifierValueText = node.Identifier.ValueText;
- // If we are here in an error-recovery scenario, say, "foo(123);" then
+ // If we are here in an error-recovery scenario, say, "goo(123);" then
// we might have an 'empty' simple name. In that case do not report an
// 'unable to find ""' error; we've already reported an error in the parser so
// just bail out with an error symbol.
diff --git a/src/Compilers/CSharp/Portable/Binder/Imports.cs b/src/Compilers/CSharp/Portable/Binder/Imports.cs
index fbddb14a9548a..f618a4ab2ff03 100644
--- a/src/Compilers/CSharp/Portable/Binder/Imports.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Imports.cs
@@ -98,9 +98,9 @@ public static Imports FromSyntax(
// define all of the extern aliases first. They may used by the target of a using
- // using Bar=Foo::Bar;
- // using Foo::Baz;
- // extern alias Foo;
+ // using Bar=Goo::Bar;
+ // using Goo::Baz;
+ // extern alias Goo;
var diagnostics = new DiagnosticBag();
diff --git a/src/Compilers/CSharp/Portable/Binder/LookupResult.cs b/src/Compilers/CSharp/Portable/Binder/LookupResult.cs
index 361ef9858b9d7..b1b844a810451 100644
--- a/src/Compilers/CSharp/Portable/Binder/LookupResult.cs
+++ b/src/Compilers/CSharp/Portable/Binder/LookupResult.cs
@@ -35,7 +35,7 @@ namespace Microsoft.CodeAnalysis.CSharp
///
/// var result = LookupResult.GetInstance();
///
- /// scope.Lookup(result, "foo");
+ /// scope.Lookup(result, "goo");
/// ... use result ...
///
/// result.Clear();
diff --git a/src/Compilers/CSharp/Portable/Binder/LookupResultKind.cs b/src/Compilers/CSharp/Portable/Binder/LookupResultKind.cs
index 111dc9c7a7241..eed16b24e6147 100644
--- a/src/Compilers/CSharp/Portable/Binder/LookupResultKind.cs
+++ b/src/Compilers/CSharp/Portable/Binder/LookupResultKind.cs
@@ -41,7 +41,7 @@ internal enum LookupResultKind : byte
WrongArity,
NotCreatable, // E.g., new of an interface or static class
Inaccessible,
- NotReferencable, // E.g., get_Foo binding to an accessor.
+ NotReferencable, // E.g., get_Goo binding to an accessor.
NotAValue,
NotAVariable, // used for several slightly different places, e.g. LHS of =, out/ref parameters, etc.
NotInvocable,
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs
index 0caaa511c1c8b..82d925b8649c8 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs
@@ -480,7 +480,7 @@ private static bool InheritsFromIgnoringConstruction(
}
// NOTE(cyrusn): The base type of an 'original' type may not be 'original'. i.e.
- // "class Foo : IBar". We must map it back to the 'original' when as we walk up
+ // "class Goo : IBar". We must map it back to the 'original' when as we walk up
// the base type hierarchy.
var next = current.GetNextBaseTypeNoUseSiteDiagnostics(basesBeingResolved, compilation, ref visited);
if ((object)next == null)
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
index d13b65158e3c4..01f75f0457288 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
@@ -148,7 +148,7 @@ public static MethodTypeInferenceResult Infer(
// void M(T t, U u);
// }
// ...
- // static void Foo(V v, I iv)
+ // static void Goo(V v, I iv)
// {
// iv.M(v, "");
// }
@@ -546,7 +546,7 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression
// that has no name text. This is because of the following scenario:
//
// void M(T t) { }
- // void Foo()
+ // void Goo()
// {
// UnknownType t;
// M(t);
@@ -2713,17 +2713,17 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray
+ // x.Goo( y=>
//
- // and the question is, "is Foo a valid extension method of x?" If Foo is
- // generic, then Foo will be something like:
+ // and the question is, "is Goo a valid extension method of x?" If Goo is
+ // generic, then Goo will be something like:
//
- // static Blah Foo(this Bar bar, Func f){ ... }
+ // static Blah Goo(this Bar bar, Func f){ ... }
//
// What we would like to know is: given _only_ the expression x, can we infer
// what T is in Bar ? If we can, then for error recovery and reporting
- // we can provisionally consider Foo to be an extension method of x. If we
- // cannot deduce this just from x then we should consider Foo to not be an
+ // we can provisionally consider Goo to be an extension method of x. If we
+ // cannot deduce this just from x then we should consider Goo to not be an
// extension method of x, at least until we have more information.
//
// Clearly it is pointless to run multiple phases
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
index dda38e3b6c1d0..a5a3121d29fc5 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
@@ -571,8 +571,8 @@ private void AddMemberToCandidateSet(
// If the normal form is invalid and the expanded form is valid then obviously we prefer
// the expanded form. However, there may be error-reporting situations where we
// prefer to report the error on the expanded form rather than the normal form.
- // For example, if you have something like Foo(params T[]) and a call
- // Foo(1, "") then the error for the normal form is "too many arguments"
+ // For example, if you have something like Goo(params T[]) and a call
+ // Goo(1, "") then the error for the normal form is "too many arguments"
// and the error for the expanded form is "failed to infer T". Clearly the
// expanded form error is better.
private static bool PreferExpandedFormOverNormalForm(MemberAnalysisResult normalResult, MemberAnalysisResult expandedResult)
@@ -904,21 +904,21 @@ private static void RemoveAllInterfaceMembers(ArrayBuilder(U u) where U : C, IFoo { u.ToString(); } // ???
+ // void M(U u) where U : C, IGoo { u.ToString(); } // ???
// ...
// M(new D());
//
// What should overload resolution do on the call to u.ToString()?
//
- // We will have IFoo.ToString and C.ToString (which is an override of object.ToString)
+ // We will have IGoo.ToString and C.ToString (which is an override of object.ToString)
// in the candidate set. Does the rule apply to eliminate all interface methods? NO. The
// rule only applies if the candidate set contains a method which originally came from a
// class type other than object. The method C.ToString is the "slot" for
diff --git a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs
index 25cd68f968b69..66ad37bc0006e 100644
--- a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs
+++ b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs
@@ -61,7 +61,7 @@ internal CSharpParseOptions(
LanguageVersion languageVersion,
DocumentationMode documentationMode,
SourceCodeKind kind,
- IEnumerable preprocessorSymbols,
+ ImmutableArray preprocessorSymbols,
IReadOnlyDictionary features)
: base(kind, documentationMode)
{
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
index 326b2cba11838..c8bcf583493c1 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
+++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
@@ -2375,7 +2375,7 @@ internal static string ERR_CannotDeconstructDynamic {
}
///
- /// Looks up a localized string similar to /embed switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded)..
+ /// Looks up a localized string similar to /embed switch is only supported when emitting a PDB..
///
internal static string ERR_CannotEmbedWithoutPdb {
get {
@@ -3733,6 +3733,15 @@ internal static string ERR_DynamicLocalFunctionParamsParameter {
}
}
+ ///
+ /// Looks up a localized string similar to Cannot pass argument with dynamic type to generic local function '{0}' with inferred type arguments..
+ ///
+ internal static string ERR_DynamicLocalFunctionTypeParameter {
+ get {
+ return ResourceManager.GetString("ERR_DynamicLocalFunctionTypeParameter", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to One or more types required to compile a dynamic expression cannot be found. Are you missing a reference?.
///
@@ -8324,7 +8333,7 @@ internal static string ERR_RefReturnParameter {
}
///
- /// Looks up a localized string similar to Cannot return or a member of parameter '{0}' by reference because it is not a ref or out parameter.
+ /// Looks up a localized string similar to Cannot return a member of parameter '{0}' by reference because it is not a ref or out parameter.
///
internal static string ERR_RefReturnParameter2 {
get {
@@ -8756,7 +8765,7 @@ internal static string ERR_SourceLinkRequiresPdb {
}
///
- /// Looks up a localized string similar to Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method.
+ /// Looks up a localized string similar to Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method.
///
internal static string ERR_SpecialByRefInLambda {
get {
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index cdcd79100c183..4bc2a7e523086 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -3894,7 +3894,7 @@ You should consider suppressing the warning only if you're sure that you don't w
Indexed property '{0}' must have all arguments optional
- Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method
+ Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async methodFirst argument to a security attribute must be a valid SecurityAction
@@ -4806,7 +4806,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Cannot return a parameter by reference '{0}' because it is not a ref or out parameter
- Cannot return or a member of parameter '{0}' by reference because it is not a ref or out parameter
+ Cannot return a member of parameter '{0}' by reference because it is not a ref or out parameterCannot return local '{0}' by reference because it is not a ref local
@@ -4974,7 +4974,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
/sourcelink switch is only supported when emitting PDB.
- /embed switch is only supported when emitting Portable PDB (/debug:portable or /debug:embedded).
+ /embed switch is only supported when emitting a PDB.Invalid instrumentation kind: {0}
@@ -5096,6 +5096,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Unable to read debug information of method '{0}' (token 0x{1:X8}) from assembly '{2}'
+
+ Cannot pass argument with dynamic type to generic local function '{0}' with inferred type arguments.
+
default interface implementation
diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
index 5cb919f64b05f..6a33e6f464c36 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/EmitAddress.cs
@@ -221,7 +221,7 @@ private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind
// when a sequence is happened to be a byref receiver
// we may need to extend the life time of the target until we are done accessing it
- // {.v ; v = Foo(); v}.Bar() // v should be released only after Bar() is done.
+ // {.v ; v = Goo(); v}.Bar() // v should be released only after Bar() is done.
LocalSymbol doNotRelease = null;
if (tempOpt == null)
{
diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
index dd8c54d47e34a..49a41f5b032a8 100644
--- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
+++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
@@ -28,7 +28,7 @@ internal class Optimizer
/// 1) Do not elide any user defined locals, even if never read from.
/// Example:
/// {
- /// var dummy = Foo(); // should not become just "Foo"
+ /// var dummy = Goo(); // should not become just "Goo"
/// }
///
/// User might want to examine dummy in the debugger.
@@ -36,8 +36,8 @@ internal class Optimizer
/// 2) Do not carry values on the stack between statements
/// Example:
/// {
- /// var temp = Foo();
- /// temp.ToString(); // should not become Foo().ToString();
+ /// var temp = Goo();
+ /// temp.ToString(); // should not become Goo().ToString();
/// }
///
/// User might want to examine temp in the debugger.
@@ -368,7 +368,7 @@ private bool Includes(int val)
// it will affect inference of stack behavior
// it will also affect when locals can be scheduled to the stack
// Example:
- // Foo(x, ref x) <-- x cannot be a stack local as it is used in different contexts.
+ // Goo(x, ref x) <-- x cannot be a stack local as it is used in different contexts.
internal enum ExprContext
{
None,
diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs
index 256dd9ebf02c4..134d914809622 100644
--- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs
+++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs
@@ -1243,14 +1243,9 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar
embeddedFiles.AddRange(sourceFiles);
}
- if (embeddedFiles.Count > 0)
+ if (embeddedFiles.Count > 0 && !emitPdb)
{
- // Restricted to portable PDBs for now, but the IsPortable condition should be removed
- // and the error message adjusted accordingly when native PDB support is added.
- if (!emitPdb || !debugInformationFormat.IsPortable())
- {
- AddDiagnostic(diagnostics, ErrorCode.ERR_CannotEmbedWithoutPdb);
- }
+ AddDiagnostic(diagnostics, ErrorCode.ERR_CannotEmbedWithoutPdb);
}
var parsedFeatures = ParseFeatures(features);
diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
index 8170e947bd854..275e31e26d9a1 100644
--- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
@@ -1002,8 +1002,8 @@ private void GetAllUnaliasedModules(ArrayBuilder modules)
/// Return a list of assembly symbols than can be accessed without using an alias.
/// For example:
/// 1) /r:A.dll /r:B.dll -> A, B
- /// 2) /r:Foo=A.dll /r:B.dll -> B
- /// 3) /r:Foo=A.dll /r:A.dll -> A
+ /// 2) /r:Goo=A.dll /r:B.dll -> B
+ /// 3) /r:Goo=A.dll /r:A.dll -> A
///
internal void GetUnaliasedReferencedAssemblies(ArrayBuilder assemblies)
{
diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
index cf7822cbd708b..d634bd24e49cd 100644
--- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
@@ -3323,7 +3323,7 @@ private ImmutableArray GetSemanticSymbols(BoundExpression boundNode,
var containingMember = binder.ContainingMember();
var thisParam = GetThisParameter(boundNode.Type, containingType, containingMember, out resultKind);
- symbols = ImmutableArray.Create(thisParam);
+ symbols = thisParam != null ? ImmutableArray.Create(thisParam) : ImmutableArray.Empty;
}
break;
diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
index f5da50993a98e..1158c38b90530 100644
--- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs
@@ -1874,7 +1874,7 @@ protected CSharpSyntaxNode GetBindableParentNode(CSharpSyntaxNode node)
/// and returns it instead of rebinding it.
///
/// For example, we might have:
- /// while (x > foo())
+ /// while (x > goo())
/// {
/// y = y * x;
/// z = z + y;
diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs
index 733c3f3f9428f..66342e3b1f70c 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs
@@ -137,12 +137,12 @@ internal override bool SynthesizesLoweredBoundBody
///
/// Given a SynthesizedExplicitImplementationMethod (effectively a tuple (interface method, implementing method, implementing type)),
- /// construct a BoundBlock body. Consider the tuple (Interface.Foo, Base.Foo, Derived). The generated method will look like:
+ /// construct a BoundBlock body. Consider the tuple (Interface.Goo, Base.Goo, Derived). The generated method will look like:
///
- /// R Interface.Foo<T1, T2, ...>(A1 a1, A2 a2, ...)
+ /// R Interface.Goo<T1, T2, ...>(A1 a1, A2 a2, ...)
/// {
/// //don't return the output if the return type is void
- /// return this.Foo<T1, T2, ...>(a1, a2, ...);
+ /// return this.Goo<T1, T2, ...>(a1, a2, ...);
/// }
///
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
index 9d166d4406470..db4b5be579fc8 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
@@ -660,7 +660,7 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState)
var diagnosticsThisMethod = DiagnosticBag.GetInstance();
var method = methodWithBody.Method;
- var lambda = method as SynthesizedLambdaMethod;
+ var lambda = method as SynthesizedClosureMethod;
var variableSlotAllocatorOpt = ((object)lambda != null) ?
_moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(lambda, lambda.TopLevelMethod, diagnosticsThisMethod) :
_moduleBeingBuiltOpt.TryCreateVariableSlotAllocator(method, method, diagnosticsThisMethod);
diff --git a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs
index a81b758e6f017..785b20d1f24d7 100644
--- a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs
@@ -66,7 +66,7 @@ internal MethodWithBody(MethodSymbol method, BoundStatement body, ImportChain im
public readonly CSharpCompilation Compilation;
- public LambdaFrame StaticLambdaFrame;
+ public SynthesizedClosureEnvironment StaticLambdaFrame;
///
/// A graph of method->method references for this(...) constructor initializers.
diff --git a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpDefinitionMap.cs b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpDefinitionMap.cs
index c847f27478cb8..8652ceec81d5c 100644
--- a/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpDefinitionMap.cs
+++ b/src/Compilers/CSharp/Portable/Emitter/EditAndContinue/CSharpDefinitionMap.cs
@@ -183,16 +183,13 @@ protected override void GetStateMachineFieldMapFromMetadata(
awaiterSlotCount = maxAwaiterSlotIndex + 1;
}
- protected override ImmutableArray TryGetLocalSlotMapFromMetadata(MethodDefinitionHandle handle, EditAndContinueMethodDebugInformation debugInfo)
+ protected override ImmutableArray GetLocalSlotMapFromMetadata(StandaloneSignatureHandle handle, EditAndContinueMethodDebugInformation debugInfo)
{
- ImmutableArray> slotMetadata;
- if (!_metadataDecoder.TryGetLocals(handle, out slotMetadata))
- {
- return default(ImmutableArray);
- }
+ Debug.Assert(!handle.IsNil);
- var result = CreateLocalSlotMap(debugInfo, slotMetadata);
- Debug.Assert(result.Length == slotMetadata.Length);
+ var localInfos = _metadataDecoder.GetLocalsOrThrow(handle);
+ var result = CreateLocalSlotMap(debugInfo, localInfos);
+ Debug.Assert(result.Length == localInfos.Length);
return result;
}
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index 2754aab597002..4eaa4ff575e6f 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -1496,6 +1496,7 @@ internal enum ErrorCode
#region diagnostics introduced for C# 7.2
ERR_FeatureNotAvailableInVersion7_2 = 8320,
WRN_UnreferencedLocalFunction = 8321,
+ ERR_DynamicLocalFunctionTypeParameter = 8322,
#endregion diagnostics introduced for C# 7.2
ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation = 8501,
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/ExitPointsWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/ExitPointsWalker.cs
index 62a795d91cbbc..a3da2ef30d924 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/ExitPointsWalker.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/ExitPointsWalker.cs
@@ -127,13 +127,13 @@ override protected void LeaveRegion()
switch (pending.Branch.Kind)
{
case BoundKind.GotoStatement:
- if (_labelsInside.Contains(((pending.Branch) as BoundGotoStatement).Label)) continue;
+ if (_labelsInside.Contains(((BoundGotoStatement)pending.Branch).Label)) continue;
break;
case BoundKind.BreakStatement:
- if (_labelsInside.Contains(((pending.Branch) as BoundBreakStatement).Label)) continue;
+ if (_labelsInside.Contains(((BoundBreakStatement)pending.Branch).Label)) continue;
break;
case BoundKind.ContinueStatement:
- if (_labelsInside.Contains(((pending.Branch) as BoundContinueStatement).Label)) continue;
+ if (_labelsInside.Contains(((BoundContinueStatement)pending.Branch).Label)) continue;
break;
case BoundKind.YieldBreakStatement:
case BoundKind.ReturnStatement:
diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs
index adeaf9b8085cf..d5d552accd5c5 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs
@@ -2156,7 +2156,7 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node)
{
if (node.ReceiverOpt != null)
{
- // An explicit or implicit receiver, for example in an expression such as (x.Foo is Action, or Foo is Action), is considered to be read.
+ // An explicit or implicit receiver, for example in an expression such as (x.Goo is Action, or Goo is Action), is considered to be read.
VisitRvalue(node.ReceiverOpt);
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs
index 7ed469023e18e..8e9d7f7adf4b6 100644
--- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs
@@ -197,26 +197,26 @@ private static bool IsSameLocalOrField(BoundExpression expr1, BoundExpression ex
switch (expr1.Kind)
{
case BoundKind.Local:
- var local1 = expr1 as BoundLocal;
- var local2 = expr2 as BoundLocal;
+ var local1 = (BoundLocal)expr1;
+ var local2 = (BoundLocal)expr2;
return local1.LocalSymbol == local2.LocalSymbol;
case BoundKind.FieldAccess:
- var field1 = expr1 as BoundFieldAccess;
- var field2 = expr2 as BoundFieldAccess;
+ var field1 = (BoundFieldAccess)expr1;
+ var field2 = (BoundFieldAccess)expr2;
return field1.FieldSymbol == field2.FieldSymbol &&
(field1.FieldSymbol.IsStatic || IsSameLocalOrField(field1.ReceiverOpt, field2.ReceiverOpt));
case BoundKind.EventAccess:
- var event1 = expr1 as BoundEventAccess;
- var event2 = expr2 as BoundEventAccess;
+ var event1 = (BoundEventAccess)expr1;
+ var event2 = (BoundEventAccess)expr2;
return event1.EventSymbol == event2.EventSymbol &&
(event1.EventSymbol.IsStatic || IsSameLocalOrField(event1.ReceiverOpt, event2.ReceiverOpt));
case BoundKind.Parameter:
- var param1 = expr1 as BoundParameter;
- var param2 = expr2 as BoundParameter;
+ var param1 = (BoundParameter)expr1;
+ var param2 = (BoundParameter)expr2;
return param1.ParameterSymbol == param2.ParameterSymbol;
case BoundKind.RangeVariable:
- var rangeVar1 = expr1 as BoundRangeVariable;
- var rangeVar2 = expr2 as BoundRangeVariable;
+ var rangeVar1 = (BoundRangeVariable)expr1;
+ var rangeVar2 = (BoundRangeVariable)expr2;
return rangeVar1.RangeVariableSymbol == rangeVar2.RangeVariableSymbol;
case BoundKind.ThisReference:
case BoundKind.PreviousSubmissionReference:
diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs
index ca99d9de6f630..32b7a2c671bd8 100644
--- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorRewriter.cs
@@ -172,7 +172,7 @@ protected override void GenerateControlFields()
// Add a field: T current
_currentField = F.StateMachineField(_elementType, GeneratedNames.MakeIteratorCurrentFieldName());
- // if it is an enumerable, and either Environment.CurrentManagedThreadId or System.Thread are available
+ // if it is an enumerable, and either Environment.CurrentManagedThreadId or Thread.ManagedThreadId are available
// add a field: int initialThreadId
bool addInitialThreadId =
_isEnumerable &&
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs
index 80f1261743776..6ac017446b934 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs
@@ -31,7 +31,7 @@ private LambdaCapturedVariable(SynthesizedContainer frame, TypeSymbol type, stri
_isThis = isThisParameter;
}
- public static LambdaCapturedVariable Create(LambdaFrame frame, Symbol captured, ref int uniqueId)
+ public static LambdaCapturedVariable Create(SynthesizedClosureEnvironment frame, Symbol captured, ref int uniqueId)
{
Debug.Assert(captured is LocalSymbol || captured is ParameterSymbol);
@@ -87,7 +87,7 @@ private static TypeSymbol GetCapturedVariableFieldType(SynthesizedContainer fram
if ((object)local != null)
{
// if we're capturing a generic frame pointer, construct it with the new frame's type parameters
- var lambdaFrame = local.Type.OriginalDefinition as LambdaFrame;
+ var lambdaFrame = local.Type.OriginalDefinition as SynthesizedClosureEnvironment;
if ((object)lambdaFrame != null)
{
// lambdaFrame may have less generic type parameters than frame, so trim them down (the first N will always match)
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.Tree.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.Tree.cs
index b06955cf638ba..769035d013dce 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.Tree.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.Tree.cs
@@ -4,10 +4,10 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
+using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
-using System.Linq;
namespace Microsoft.CodeAnalysis.CSharp
{
@@ -26,21 +26,27 @@ internal sealed partial class Analysis : BoundTreeWalkerWithStackGuardWithoutRec
[DebuggerDisplay("{ToString(), nq}")]
public sealed class Scope
{
- public Scope Parent { get; }
+ public readonly Scope Parent;
- public ArrayBuilder NestedScopes { get; } = ArrayBuilder.GetInstance();
+ public readonly ArrayBuilder NestedScopes = ArrayBuilder.GetInstance();
///
/// A list of all closures (all lambdas and local functions) declared in this scope.
///
- public ArrayBuilder Closures { get; } = ArrayBuilder.GetInstance();
+ public readonly ArrayBuilder Closures = ArrayBuilder.GetInstance();
///
/// A list of all locals or parameters that were declared in this scope and captured
/// in this scope or nested scopes. "Declared" refers to the start of the variable
/// lifetime (which, at this point in lowering, should be equivalent to lexical scope).
///
- public ArrayBuilder DeclaredVariables { get; } = ArrayBuilder.GetInstance();
+ ///
+ /// It's important that this is a set and that enumeration order is deterministic. We loop
+ /// over this list to generate proxies and if we loop out of order this will cause
+ /// non-deterministic compilation, and if we generate duplicate proxies we'll generate
+ /// wasteful code in the best case and incorrect code in the worst.
+ ///
+ public readonly SetWithInsertionOrder DeclaredVariables = new SetWithInsertionOrder();
///
/// The bound node representing this scope. This roughly corresponds to the bound
@@ -48,13 +54,19 @@ public sealed class Scope
/// methods/closures are introduced into their Body's scope and do not get their
/// own scope.
///
- public BoundNode BoundNode { get; }
+ public readonly BoundNode BoundNode;
///
/// The closure that this scope is nested inside. Null if this scope is not nested
/// inside a closure.
///
- public Closure ContainingClosure { get; }
+ public readonly Closure ContainingClosureOpt;
+
+ ///
+ /// Environments created in this scope to hold .
+ ///
+ public readonly ArrayBuilder DeclaredEnvironments
+ = ArrayBuilder.GetInstance();
public Scope(Scope parent, BoundNode boundNode, Closure containingClosure)
{
@@ -62,7 +74,7 @@ public Scope(Scope parent, BoundNode boundNode, Closure containingClosure)
Parent = parent;
BoundNode = boundNode;
- ContainingClosure = containingClosure;
+ ContainingClosureOpt = containingClosure;
}
public void Free()
@@ -78,7 +90,7 @@ public void Free()
closure.Free();
}
Closures.Free();
- DeclaredVariables.Free();
+ DeclaredEnvironments.Free();
}
public override string ToString() => BoundNode.Syntax.GetText().ToString();
@@ -98,79 +110,76 @@ public sealed class Closure
///
/// The method symbol for the original lambda or local function.
///
- public MethodSymbol OriginalMethodSymbol { get; }
+ public readonly MethodSymbol OriginalMethodSymbol;
+
+ ///
+ /// Syntax for the block of the nested function.
+ ///
+ public readonly SyntaxReference BlockSyntax;
+
+ public readonly PooledHashSet CapturedVariables = PooledHashSet.GetInstance();
+
+ public readonly ArrayBuilder CapturedEnvironments
+ = ArrayBuilder.GetInstance();
+
+ public ClosureEnvironment ContainingEnvironmentOpt;
+
+ private bool _capturesThis;
+
+ ///
+ /// True if this closure directly or transitively captures 'this' (captures
+ /// a local function which directly or indirectly captures 'this').
+ /// Calculated in .
+ ///
+ public bool CapturesThis
+ {
+ get => _capturesThis;
+ set
+ {
+ Debug.Assert(value);
+ _capturesThis = value;
+ }
+ }
- public PooledHashSet CapturedVariables { get; } = PooledHashSet.GetInstance();
+ public SynthesizedClosureMethod SynthesizedLoweredMethod;
- public Closure(MethodSymbol symbol)
+ public Closure(MethodSymbol symbol, SyntaxReference blockSyntax)
{
Debug.Assert(symbol != null);
OriginalMethodSymbol = symbol;
+ BlockSyntax = blockSyntax;
}
public void Free()
{
CapturedVariables.Free();
+ CapturedEnvironments.Free();
}
}
- ///
- /// Optimizes local functions such that if a local function only references other local functions
- /// that capture no variables, we don't need to create capture environments for any of them.
- ///
- private void RemoveUnneededReferences()
+ public sealed class ClosureEnvironment
{
- var methodGraph = new MultiDictionary();
- var capturesThis = new HashSet();
- var capturesVariable = new HashSet();
- var visitStack = new Stack();
- VisitClosures(ScopeTree, (scope, closure) =>
- {
- foreach (var capture in closure.CapturedVariables)
- {
- if (capture is MethodSymbol localFunc)
- {
- methodGraph.Add(localFunc, closure.OriginalMethodSymbol);
- }
- else if (capture == _topLevelMethod.ThisParameter)
- {
- if (capturesThis.Add(closure.OriginalMethodSymbol))
- {
- visitStack.Push(closure.OriginalMethodSymbol);
- }
- }
- else if (capturesVariable.Add(closure.OriginalMethodSymbol) &&
- !capturesThis.Contains(closure.OriginalMethodSymbol))
- {
- visitStack.Push(closure.OriginalMethodSymbol);
- }
- }
- });
+ public readonly SetWithInsertionOrder CapturedVariables;
+
+ ///
+ /// True if this environment captures a reference to a class environment
+ /// declared in a higher scope. Assigned by
+ ///
+ ///
+ public bool CapturesParent;
- while (visitStack.Count > 0)
- {
- var current = visitStack.Pop();
- var setToAddTo = capturesVariable.Contains(current) ? capturesVariable : capturesThis;
- foreach (var capturesCurrent in methodGraph[current])
- {
- if (setToAddTo.Add(capturesCurrent))
- {
- visitStack.Push(capturesCurrent);
- }
- }
- }
+ public readonly bool IsStruct;
+ internal SynthesizedClosureEnvironment SynthesizedEnvironment;
- VisitClosures(ScopeTree, (scope, closure) =>
+ public ClosureEnvironment(IEnumerable capturedVariables, bool isStruct)
{
- if (!capturesVariable.Contains(closure.OriginalMethodSymbol))
- {
- closure.CapturedVariables.Clear();
- }
- if (capturesThis.Contains(closure.OriginalMethodSymbol))
+ CapturedVariables = new SetWithInsertionOrder();
+ foreach (var item in capturedVariables)
{
- closure.CapturedVariables.Add(_topLevelMethod.ThisParameter);
+ CapturedVariables.Add(item);
}
- });
+ IsStruct = isStruct;
+ }
}
///
@@ -189,11 +198,50 @@ public static void VisitClosures(Scope scope, Action action)
}
}
+ ///
+ /// Visit all the closures and return true when the returns
+ /// true. Otherwise, returns false.
+ ///
+ public static bool CheckClosures(Scope scope, Func func)
+ {
+ foreach (var closure in scope.Closures)
+ {
+ if (func(scope, closure))
+ {
+ return true;
+ }
+ }
+
+ foreach (var nested in scope.NestedScopes)
+ {
+ if (CheckClosures(nested, func))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Visit the tree with the given root and run the
+ ///
+ public static void VisitScopeTree(Scope treeRoot, Action action)
+ {
+ action(treeRoot);
+
+ foreach (var nested in treeRoot.NestedScopes)
+ {
+ VisitScopeTree(nested, action);
+ }
+ }
+
///
/// Builds a tree of nodes corresponding to a given method.
- /// visits the bound tree and translates
- /// information from the bound tree about variable scope, declared variables, and
- /// variable captures into the resulting tree.
+ ///
+ /// visits the bound tree and translates information from the bound tree about
+ /// variable scope, declared variables, and variable captures into the resulting
+ /// tree.
///
private class ScopeTreeBuilder : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
{
@@ -211,24 +259,49 @@ private class ScopeTreeBuilder : BoundTreeWalkerWithStackGuardWithoutRecursionOn
///
private readonly SmallDictionary _localToScope = new SmallDictionary();
- private readonly Analysis _analysis;
+ private readonly MethodSymbol _topLevelMethod;
- private ScopeTreeBuilder(Scope rootScope, Analysis analysis)
+ ///
+ /// If a local function is in the set, at some point in the code it is converted
+ /// to a delegate and should then not be optimized to a struct closure.
+ /// Also contains all lambdas (as they are converted to delegates implicitly).
+ ///
+ private readonly HashSet _methodsConvertedToDelegates;
+ private readonly DiagnosticBag _diagnostics;
+
+ private ScopeTreeBuilder(
+ Scope rootScope,
+ MethodSymbol topLevelMethod,
+ HashSet methodsConvertedToDelegates,
+ DiagnosticBag diagnostics)
{
Debug.Assert(rootScope != null);
- Debug.Assert(analysis != null);
+ Debug.Assert(topLevelMethod != null);
+ Debug.Assert(methodsConvertedToDelegates != null);
+ Debug.Assert(diagnostics != null);
_currentScope = rootScope;
- _analysis = analysis;
+ _topLevelMethod = topLevelMethod;
+ _methodsConvertedToDelegates = methodsConvertedToDelegates;
+ _diagnostics = diagnostics;
}
- public static Scope Build(BoundNode node, Analysis analysis)
+ public static Scope Build(
+ BoundNode node,
+ MethodSymbol topLevelMethod,
+ HashSet methodsConvertedToDelegates,
+ DiagnosticBag diagnostics)
{
// This should be the top-level node
Debug.Assert(node == FindNodeToAnalyze(node));
+ Debug.Assert(topLevelMethod != null);
var rootScope = new Scope(parent: null, boundNode: node, containingClosure: null);
- var builder = new ScopeTreeBuilder(rootScope, analysis);
+ var builder = new ScopeTreeBuilder(
+ rootScope,
+ topLevelMethod,
+ methodsConvertedToDelegates,
+ diagnostics);
builder.Build();
return rootScope;
}
@@ -236,7 +309,13 @@ public static Scope Build(BoundNode node, Analysis analysis)
private void Build()
{
// Set up the current method locals
- DeclareLocals(_currentScope, _analysis._topLevelMethod.Parameters);
+ DeclareLocals(_currentScope, _topLevelMethod.Parameters);
+ // Treat 'this' as a formal parameter of the top-level method
+ if (_topLevelMethod.TryGetThisParameter(out var thisParam))
+ {
+ DeclareLocals(_currentScope, ImmutableArray.Create(thisParam));
+ }
+
Visit(_currentScope.BoundNode);
}
@@ -245,12 +324,6 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node)
public override BoundNode VisitBlock(BoundBlock node)
{
- if (node.Locals.IsDefaultOrEmpty)
- {
- // Skip introducing a new scope if there are no new locals
- return base.VisitBlock(node);
- }
-
var oldScope = _currentScope;
_currentScope = CreateOrReuseScope(node, node.Locals);
var result = base.VisitBlock(node);
@@ -278,12 +351,6 @@ public override BoundNode VisitSequence(BoundSequence node)
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
{
- if (node.InnerLocals.IsDefaultOrEmpty)
- {
- // Skip introducing a new scope if there are no new locals
- return base.VisitSwitchStatement(node);
- }
-
var oldScope = _currentScope;
_currentScope = CreateOrReuseScope(node, node.InnerLocals);
var result = base.VisitSwitchStatement(node);
@@ -296,7 +363,7 @@ public override BoundNode VisitLambda(BoundLambda node)
var oldInExpressionTree = _inExpressionTree;
_inExpressionTree |= node.Type.IsExpressionTree();
- _analysis.MethodsConvertedToDelegates.Add(node.Symbol.OriginalDefinition);
+ _methodsConvertedToDelegates.Add(node.Symbol.OriginalDefinition);
var result = VisitClosure(node.Symbol, node.Body);
_inExpressionTree = oldInExpressionTree;
@@ -311,7 +378,7 @@ public override BoundNode VisitCall(BoundCall node)
if (node.Method.MethodKind == MethodKind.LocalFunction)
{
// Use OriginalDefinition to strip generic type parameters
- AddIfCaptured(node.Method.OriginalDefinition);
+ AddIfCaptured(node.Method.OriginalDefinition, node.Syntax);
}
return base.VisitCall(node);
}
@@ -322,36 +389,36 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE
{
// Use OriginalDefinition to strip generic type parameters
var method = node.MethodOpt.OriginalDefinition;
- AddIfCaptured(method);
- _analysis.MethodsConvertedToDelegates.Add(method);
+ AddIfCaptured(method, node.Syntax);
+ _methodsConvertedToDelegates.Add(method);
}
return base.VisitDelegateCreationExpression(node);
}
public override BoundNode VisitParameter(BoundParameter node)
{
- AddIfCaptured(node.ParameterSymbol);
+ AddIfCaptured(node.ParameterSymbol, node.Syntax);
return base.VisitParameter(node);
}
public override BoundNode VisitLocal(BoundLocal node)
{
- AddIfCaptured(node.LocalSymbol);
+ AddIfCaptured(node.LocalSymbol, node.Syntax);
return base.VisitLocal(node);
}
public override BoundNode VisitBaseReference(BoundBaseReference node)
{
- AddIfCaptured(_analysis._topLevelMethod.ThisParameter);
+ AddIfCaptured(_topLevelMethod.ThisParameter, node.Syntax);
return base.VisitBaseReference(node);
}
public override BoundNode VisitThisReference(BoundThisReference node)
{
- var thisParam = _analysis._topLevelMethod.ThisParameter;
+ var thisParam = _topLevelMethod.ThisParameter;
if (thisParam != null)
{
- AddIfCaptured(thisParam);
+ AddIfCaptured(thisParam, node.Syntax);
}
else
{
@@ -375,7 +442,7 @@ private BoundNode VisitClosure(MethodSymbol closureSymbol, BoundBlock body)
// Closure is declared (lives) in the parent scope, but its
// variables are in a nested scope
- var closure = new Closure(closureSymbol);
+ var closure = new Closure(closureSymbol, body.Syntax.GetReference());
_currentScope.Closures.Add(closure);
var oldClosure = _currentClosure;
@@ -402,8 +469,13 @@ private BoundNode VisitClosure(MethodSymbol closureSymbol, BoundBlock body)
return result;
}
- private void AddIfCaptured(Symbol symbol)
+ private void AddIfCaptured(Symbol symbol, SyntaxNode syntax)
{
+ Debug.Assert(
+ symbol.Kind == SymbolKind.Local ||
+ symbol.Kind == SymbolKind.Parameter ||
+ symbol.Kind == SymbolKind.Method);
+
if (_currentClosure == null)
{
// Can't be captured if we're not in a closure
@@ -416,8 +488,18 @@ private void AddIfCaptured(Symbol symbol)
return;
}
+ if (symbol is MethodSymbol method &&
+ _currentClosure.OriginalMethodSymbol == method)
+ {
+ // Is this recursion? If so there's no capturing
+ return;
+ }
+
if (symbol.ContainingSymbol != _currentClosure.OriginalMethodSymbol)
{
+ // Restricted types can't be hoisted, so they are not permitted to be captured
+ AddDiagnosticIfRestrictedType(symbol, syntax);
+
// Record the captured variable where it's captured
var scope = _currentScope;
var closure = _currentClosure;
@@ -426,11 +508,11 @@ private void AddIfCaptured(Symbol symbol)
closure.CapturedVariables.Add(symbol);
// Also mark captured in enclosing scopes
- while (scope.ContainingClosure == closure)
+ while (scope.ContainingClosureOpt == closure)
{
scope = scope.Parent;
}
- closure = scope.ContainingClosure;
+ closure = scope.ContainingClosureOpt;
}
// Also record where the captured variable lives
@@ -442,12 +524,6 @@ private void AddIfCaptured(Symbol symbol)
return;
}
- // The 'this' parameter isn't declared in method scope
- if (symbol is ParameterSymbol param && param.IsThis)
- {
- return;
- }
-
if (_localToScope.TryGetValue(symbol, out var declScope))
{
declScope.DeclaredVariables.Add(symbol);
@@ -461,6 +537,33 @@ private void AddIfCaptured(Symbol symbol)
}
}
+ ///
+ /// Add a diagnostic if the type of a captured variable is a restricted type
+ ///
+ private void AddDiagnosticIfRestrictedType(Symbol capturedVariable, SyntaxNode syntax)
+ {
+ TypeSymbol type;
+ switch (capturedVariable.Kind)
+ {
+ case SymbolKind.Local:
+ type = ((LocalSymbol)capturedVariable).Type;
+ break;
+ case SymbolKind.Parameter:
+ type = ((ParameterSymbol)capturedVariable).Type;
+ break;
+ default:
+ // This should only be called for captured variables, and captured
+ // variables must be a method, parameter, or local symbol
+ Debug.Assert(capturedVariable.Kind == SymbolKind.Method);
+ return;
+ }
+
+ if (type.IsRestrictedType() == true)
+ {
+ _diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, type);
+ }
+ }
+
///
/// Create a new nested scope under the current scope, or reuse the current
/// scope if there's no change in the bound node for the nested scope.
@@ -469,14 +572,20 @@ private void AddIfCaptured(Symbol symbol)
private Scope CreateOrReuseScope(BoundNode node, ImmutableArray locals)
where TSymbol : Symbol
{
- // We should never create a new scope with the same bound
- // node. We can get into this situation for methods and
- // closures where a new scope is created to add parameters
- // and a new scope would be created for the method block,
- // despite the fact that they should be the same scope.
- var scope = _currentScope.BoundNode == node
- ? _currentScope
- : CreateNestedScope(node, _currentScope, _currentClosure);
+ Scope scope;
+ if (locals.IsEmpty || _currentScope.BoundNode == node)
+ {
+ // We should never create a new scope with the same bound
+ // node. We can get into this situation for methods and
+ // closures where a new scope is created to add parameters
+ // and a new scope would be created for the method block,
+ // despite the fact that they should be the same scope.
+ scope = _currentScope;
+ }
+ else
+ {
+ scope = CreateNestedScope(node, _currentScope, _currentClosure);
+ }
DeclareLocals(scope, locals);
return scope;
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs
index c89d9dee02940..216cca9ee6d9e 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.Analysis.cs
@@ -4,6 +4,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
+using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
@@ -16,50 +17,13 @@ internal partial class LambdaRewriter
/// Perform a first analysis pass in preparation for removing all lambdas from a method body. The entry point is Analyze.
/// The results of analysis are placed in the fields seenLambda, blockParent, variableBlock, captured, and captures.
///
- internal sealed partial class Analysis : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
+ internal sealed partial class Analysis
{
- private readonly MethodSymbol _topLevelMethod;
-
- private MethodSymbol _currentParent;
- private BoundNode _currentScope;
-
- ///
- /// Set to true while we are analyzing the interior of an expression lambda.
- ///
- private bool _inExpressionLambda;
-
- ///
- /// Set to true of any lambda expressions were seen in the analyzed method body.
- ///
- public bool SeenLambda { get; private set; }
-
- ///
- /// For each scope that defines variables, identifies the nearest enclosing scope that defines variables.
- ///
- public readonly Dictionary ScopeParent = new Dictionary();
-
- ///
- /// For each captured variable, identifies the scope in which it will be moved to a frame class. This is
- /// normally the node where the variable is introduced, but method parameters are moved
- /// to a frame class within the body of the method.
- ///
- public readonly Dictionary VariableScope = new Dictionary();
-
- ///
- /// For each value in variableScope, identifies the closest owning method, lambda, or local function.
- ///
- public readonly Dictionary ScopeOwner = new Dictionary();
-
- ///
- /// The syntax nodes associated with each captured variable.
- ///
- public MultiDictionary CapturedVariables = new MultiDictionary();
-
///
/// If a local function is in the set, at some point in the code it is converted to a delegate and should then not be optimized to a struct closure.
/// Also contains all lambdas (as they are converted to delegates implicitly).
///
- public readonly HashSet MethodsConvertedToDelegates = new HashSet();
+ public readonly PooledHashSet MethodsConvertedToDelegates;
///
/// True if the method signature can be rewritten to contain ref/out parameters.
@@ -69,86 +33,68 @@ public bool CanTakeRefParameters(MethodSymbol closure) => !(closure.IsAsync
// We can't rewrite delegate signatures
|| MethodsConvertedToDelegates.Contains(closure));
- ///
- /// Any scope that a method that doesn't close over.
- /// If a scope is in this set, don't use a struct closure.
- ///
- public readonly HashSet ScopesThatCantBeStructs = new HashSet();
-
- ///
- /// Blocks that are positioned between a block declaring some lifted variables
- /// and a block that contains the lambda that lifts said variables.
- /// If such block itself requires a closure, then it must lift parent frame pointer into the closure
- /// in addition to whatever else needs to be lifted.
- ///
- /// NOTE: This information is computed in addition to the regular analysis of the tree and only needed for rewriting.
- /// If someone only needs diagnostics or information about captures, this information is not necessary.
- /// needs to be called to compute this.
- ///
- public HashSet NeedsParentFrame;
-
- ///
- /// Optimized locations of lambdas.
- ///
- /// Lambda does not need to be placed in a frame that corresponds to its lexical scope if lambda does not reference any local state in that scope.
- /// It is advantageous to place lambdas higher in the scope tree, ideally in the innermost scope of all scopes that contain variables captured by a given lambda.
- /// Doing so reduces indirections needed when captured locals are accessed. For example locals from the innermost scope can be accessed with no indirection at all.
- ///
- /// NOTE: This information is computed in addition to the regular analysis of the tree and only needed for rewriting.
- /// If someone only needs diagnostics or information about captures, this information is not necessary.
- /// needs to be called to compute this.
- ///
- public Dictionary LambdaScopes;
-
///
/// The root of the scope tree for this method.
///
- public Scope ScopeTree { get; private set; }
+ public readonly Scope ScopeTree;
- private Analysis(MethodSymbol method)
+ private readonly MethodSymbol _topLevelMethod;
+ private readonly int _topLevelMethodOrdinal;
+ private readonly MethodSymbol _substitutedSourceMethod;
+ private readonly VariableSlotAllocator _slotAllocatorOpt;
+ private readonly TypeCompilationState _compilationState;
+
+ private Analysis(
+ Scope scopeTree,
+ PooledHashSet methodsConvertedToDelegates,
+ MethodSymbol topLevelMethod,
+ int topLevelMethodOrdinal,
+ MethodSymbol substitutedSourceMethod,
+ VariableSlotAllocator slotAllocatorOpt,
+ TypeCompilationState compilationState)
{
- Debug.Assert((object)method != null);
-
- _currentParent = _topLevelMethod = method;
+ ScopeTree = scopeTree;
+ MethodsConvertedToDelegates = methodsConvertedToDelegates;
+ _topLevelMethod = topLevelMethod;
+ _topLevelMethodOrdinal = topLevelMethodOrdinal;
+ _substitutedSourceMethod = substitutedSourceMethod;
+ _slotAllocatorOpt = slotAllocatorOpt;
+ _compilationState = compilationState;
}
- public static Analysis Analyze(BoundNode node, MethodSymbol method)
+ public static Analysis Analyze(
+ BoundNode node,
+ MethodSymbol method,
+ int topLevelMethodOrdinal,
+ MethodSymbol substitutedSourceMethod,
+ VariableSlotAllocator slotAllocatorOpt,
+ TypeCompilationState compilationState,
+ ArrayBuilder closureDebugInfo,
+ DiagnosticBag diagnostics)
{
- var analysis = new Analysis(method);
- analysis.Analyze(node);
+ var methodsConvertedToDelegates = PooledHashSet.GetInstance();
+ var scopeTree = ScopeTreeBuilder.Build(
+ node,
+ method,
+ methodsConvertedToDelegates,
+ diagnostics);
+ Debug.Assert(scopeTree != null);
+
+ var analysis = new Analysis(
+ scopeTree,
+ methodsConvertedToDelegates,
+ method,
+ topLevelMethodOrdinal,
+ substitutedSourceMethod,
+ slotAllocatorOpt,
+ compilationState);
+
+ analysis.MakeAndAssignEnvironments();
+ analysis.ComputeLambdaScopesAndFrameCaptures(method.ThisParameter);
+ analysis.InlineThisOnlyEnvironments();
return analysis;
}
- private void Analyze(BoundNode node)
- {
- ScopeTree = ScopeTreeBuilder.Build(node, this);
- _currentScope = FindNodeToAnalyze(node);
-
- Debug.Assert(!_inExpressionLambda);
- Debug.Assert((object)_topLevelMethod != null);
- Debug.Assert((object)_currentParent != null);
-
- foreach (ParameterSymbol parameter in _topLevelMethod.Parameters)
- {
- // parameters are counted as if they are inside the block
- VariableScope[parameter] = _currentScope;
- }
-
- Visit(node);
-
- // scopeOwner may already contain the same key/value if _currentScope is a BoundBlock.
- MethodSymbol shouldBeCurrentParent;
- if (ScopeOwner.TryGetValue(_currentScope, out shouldBeCurrentParent))
- {
- // Check to make sure the above comment is right.
- Debug.Assert(_currentParent == shouldBeCurrentParent);
- }
- else
- {
- ScopeOwner.Add(_currentScope, _currentParent);
- }
- }
-
private static BoundNode FindNodeToAnalyze(BoundNode node)
{
while (true)
@@ -179,418 +125,385 @@ private static BoundNode FindNodeToAnalyze(BoundNode node)
}
///
- /// Create the optimized plan for the location of lambda methods and whether scopes need access to parent scopes
- ///
- internal void ComputeLambdaScopesAndFrameCaptures()
+ /// Must be called only after
+ /// has been calculated.
+ ///
+ /// Finds the most optimal capture environment to place a closure in.
+ /// This roughly corresponds to the 'highest' Scope in the tree where all
+ /// the captured variables for this closure are in scope. This minimizes
+ /// the number of indirections we may have to traverse to access captured
+ /// variables.
+ ///
+ private void ComputeLambdaScopesAndFrameCaptures(ParameterSymbol thisParam)
{
- RemoveUnneededReferences();
-
- LambdaScopes = new Dictionary(ReferenceEqualityComparer.Instance);
- NeedsParentFrame = new HashSet();
-
VisitClosures(ScopeTree, (scope, closure) =>
{
- if (closure.CapturedVariables.Count > 0)
+ if (closure.CapturedEnvironments.Count > 0)
{
- (Scope innermost, Scope outermost) = FindLambdaScopeRange(closure, scope);
- RecordClosureScope(innermost, outermost, closure);
- }
- });
+ var capturedEnvs = PooledHashSet.GetInstance();
+ capturedEnvs.AddAll(closure.CapturedEnvironments);
- (Scope innermost, Scope outermost) FindLambdaScopeRange(Closure closure, Scope closureScope)
- {
- Scope innermost = null;
- Scope outermost = null;
-
- var capturedVars = PooledHashSet.GetInstance();
- capturedVars.AddAll(closure.CapturedVariables);
-
- // If any of the captured variables are local functions we'll need
- // to add the captured variables of that local function to the current
- // set. This has the effect of ensuring that if the local function
- // captures anything "above" the current scope then parent frame
- // is itself captured (so that the current lambda can call that
- // local function).
- foreach (var captured in closure.CapturedVariables)
- {
- if (captured is LocalFunctionSymbol localFunc)
+ // Find the nearest captured class environment, if one exists
+ var curScope = scope;
+ while (curScope != null)
{
- var (found, _) = GetVisibleClosure(closureScope, localFunc);
- capturedVars.AddAll(found.CapturedVariables);
+ if (capturedEnvs.RemoveAll(curScope.DeclaredEnvironments))
+ {
+ // Right now we only create one environment per scope
+ Debug.Assert(curScope.DeclaredEnvironments.Count == 1);
+ var env = curScope.DeclaredEnvironments[0];
+ if (!env.IsStruct)
+ {
+ closure.ContainingEnvironmentOpt = env;
+ break;
+ }
+ }
+ curScope = curScope.Parent;
}
- }
- for (var curScope = closureScope;
- curScope != null && capturedVars.Count > 0;
- curScope = curScope.Parent)
- {
- if (!(capturedVars.Overlaps(curScope.DeclaredVariables) ||
- capturedVars.Overlaps(curScope.Closures.Select(c => c.OriginalMethodSymbol))))
+ // Now we need to walk up the scopes to find environment captures
+ var oldEnv = curScope?.DeclaredEnvironments[0];
+ while (curScope != null)
{
- continue;
+ if (capturedEnvs.Count == 0)
+ {
+ break;
+ }
+
+ var envs = curScope.DeclaredEnvironments.Where(e => !e.IsStruct);
+ if (!envs.IsEmpty())
+ {
+ // Right now we only create one environment per scope
+ Debug.Assert(envs.IsSingle());
+ var env = envs.First();
+ Debug.Assert(!oldEnv.IsStruct);
+ oldEnv.CapturesParent = true;
+ oldEnv = env;
+ }
+ capturedEnvs.RemoveAll(curScope.DeclaredEnvironments);
+ curScope = curScope.Parent;
}
- outermost = curScope;
- if (innermost == null)
+ if (capturedEnvs.Count > 0)
{
- innermost = curScope;
+ throw ExceptionUtilities.Unreachable;
}
- capturedVars.RemoveAll(curScope.DeclaredVariables);
- capturedVars.RemoveAll(curScope.Closures.Select(c => c.OriginalMethodSymbol));
- }
+ capturedEnvs.Free();
- // If any captured variables are left, they're captured above method scope
- if (capturedVars.Count > 0)
- {
- outermost = null;
}
+ });
+ }
- capturedVars.Free();
-
- return (innermost, outermost);
+ ///
+ /// We may have ended up with a closure environment containing only
+ /// 'this'. This is basically equivalent to the containing type itself,
+ /// so we can inline the 'this' parameter into environments that
+ /// reference this one or lower closures directly onto the containing
+ /// type.
+ ///
+ private void InlineThisOnlyEnvironments()
+ {
+ // First make sure 'this' even exists
+ if (!_topLevelMethod.TryGetThisParameter(out var thisParam) ||
+ thisParam == null)
+ {
+ return;
}
- void RecordClosureScope(Scope innermost, Scope outermost, Closure closure)
+ var topLevelEnvs = ScopeTree.DeclaredEnvironments;
+
+ // If it does exist, 'this' is always in the top-level environment
+ if (topLevelEnvs.Count == 0)
{
- // 1) if there is innermost scope, lambda goes there as we cannot go any higher.
- // 2) scopes in [innermostScope, outermostScope) chain need to have access to the parent scope.
- //
- // Example:
- // if a lambda captures a method's parameter and `this`,
- // its innermost scope depth is 0 (method locals and parameters)
- // and outermost scope is -1
- // Such lambda will be placed in a closure frame that corresponds to the method's outer block
- // and this frame will also lift original `this` as a field when created by its parent.
- // Note that it is completely irrelevant how deeply the lexical scope of the lambda was originally nested.
- if (innermost != null)
- {
- LambdaScopes.Add(closure.OriginalMethodSymbol, innermost.BoundNode);
+ return;
+ }
- // Disable struct closures on methods converted to delegates, as well as on async and iterator methods.
- var markAsNoStruct = !CanTakeRefParameters(closure.OriginalMethodSymbol);
- if (markAsNoStruct)
- {
- ScopesThatCantBeStructs.Add(innermost.BoundNode);
- }
-
- while (innermost != outermost)
- {
- NeedsParentFrame.Add(innermost.BoundNode);
- innermost = innermost.Parent;
- if (markAsNoStruct && innermost != null)
- {
- ScopesThatCantBeStructs.Add(innermost.BoundNode);
- }
- }
- }
+ Debug.Assert(topLevelEnvs.Count == 1);
+ var env = topLevelEnvs[0];
+ // The environment must contain only 'this' to be inlined
+ if (env.CapturedVariables.Count > 1 ||
+ !env.CapturedVariables.Contains(thisParam))
+ {
+ return;
}
- }
- ///
- /// Walk up the scope tree looking for a closure.
- ///
- ///
- /// A tuple of the found and the it was found in.
- ///
- public static (Closure, Scope) GetVisibleClosure(Scope startingScope, MethodSymbol closureSymbol)
- {
- var currentScope = startingScope;
- while (currentScope != null)
+ if (env.IsStruct)
{
- foreach (var closure in currentScope.Closures)
+ // If everything that captures the 'this' environment
+ // lives in the containing type, we can remove the env
+ bool cantRemove = CheckClosures(ScopeTree, (scope, closure) =>
{
- if (closure.OriginalMethodSymbol == closureSymbol)
- {
- return (closure, currentScope);
- }
+ return closure.CapturedEnvironments.Contains(env) &&
+ closure.ContainingEnvironmentOpt != null;
+ });
+
+ if (!cantRemove)
+ {
+ RemoveEnv();
}
- currentScope = currentScope.Parent;
}
- throw ExceptionUtilities.Unreachable;
- }
-
- ///
- /// Finds a with a matching original symbol somewhere in the given scope or nested scopes.
- ///
- public static Closure GetClosureInTree(Scope treeRoot, MethodSymbol closureSymbol)
- {
- return Helper(treeRoot) ?? throw ExceptionUtilities.Unreachable;
-
- Closure Helper(Scope scope)
+ else
{
- foreach (var closure in scope.Closures)
+ // Class-based 'this' closures can move member functions to
+ // the top-level type and environments which capture the 'this'
+ // environment can capture 'this' directly.
+ // Note: the top-level type is treated as the initial containing
+ // environment, so by removing the 'this' environment, all
+ // nested environments which captured a pointer to the 'this'
+ // environment will now capture 'this'
+ RemoveEnv();
+ VisitClosures(ScopeTree, (scope, closure) =>
{
- if (closure.OriginalMethodSymbol == closureSymbol)
+ if (closure.ContainingEnvironmentOpt == env)
{
- return closure;
+ closure.ContainingEnvironmentOpt = null;
}
- }
+ });
+ }
- foreach (var nestedScope in scope.NestedScopes)
+ void RemoveEnv()
+ {
+ topLevelEnvs.RemoveAt(topLevelEnvs.IndexOf(env));
+ VisitClosures(ScopeTree, (scope, closure) =>
{
- var found = Helper(nestedScope);
- if (found != null)
+ var index = closure.CapturedEnvironments.IndexOf(env);
+ if (index >= 0)
{
- return found;
+ closure.CapturedEnvironments.RemoveAt(index);
}
- }
-
- return null;
+ });
}
}
- ///
- /// Compute the nesting depth of a given block.
- /// Top-most block (where method locals and parameters are defined) are at the depth 0.
- ///
- private int BlockDepth(BoundNode node)
+ private void MakeAndAssignEnvironments()
{
- // TODO: this could be precomputed and stored by analysis phase
- int result = -1;
- while (node != null)
+ VisitScopeTree(ScopeTree, scope =>
{
- result = result + 1;
- if (!ScopeParent.TryGetValue(node, out node))
+ // Currently all variables declared in the same scope are added
+ // to the same closure environment
+ var variablesInEnvironment = scope.DeclaredVariables;
+
+ // Don't create empty environments
+ if (variablesInEnvironment.Count == 0)
{
- break;
+ return;
}
- }
- return result;
- }
+ // First walk the nested scopes to find all closures which
+ // capture variables from this scope. They all need to capture
+ // this environment. This includes closures which captured local
+ // functions that capture those variables, so multiple passes may
+ // be needed. This will also decide if the environment is a struct
+ // or a class.
+ bool isStruct = true;
+ var closures = new SetWithInsertionOrder();
+ bool addedItem;
+
+ // This loop is O(n), where n is the length of the chain
+ // L_1 <- L_2 <- L_3 ...
+ // where L_1 represents a local function that directly captures the current
+ // environment, L_2 represents a local function that directly captures L_1,
+ // L_3 represents a local function that captures L_2, and so on.
+ //
+ // Each iteration of the loop runs a visitor that is proportional to the
+ // number of closures in nested scopes, so we hope that the total number
+ // of nested functions and function chains is small in any real-world code.
+ do
+ {
+ addedItem = false;
+ VisitClosures(scope, (closureScope, closure) =>
+ {
+ if (!closures.Contains(closure) &&
+ (closure.CapturedVariables.Overlaps(scope.DeclaredVariables) ||
+ closure.CapturedVariables.Overlaps(closures.Select(c => c.OriginalMethodSymbol))))
+ {
+ closures.Add(closure);
+ addedItem = true;
+ isStruct &= CanTakeRefParameters(closure.OriginalMethodSymbol);
+ }
+ });
+ } while (addedItem == true);
- public override BoundNode VisitCatchBlock(BoundCatchBlock node)
- {
- var locals = node.Locals;
+ // Next create the environment and add it to the declaration scope
+ var env = new ClosureEnvironment(variablesInEnvironment, isStruct);
+ scope.DeclaredEnvironments.Add(env);
- if (locals.IsEmpty)
- {
- return base.VisitCatchBlock(node);
- }
-
- var previousBlock = PushBlock(node, locals);
- var result = base.VisitCatchBlock(node);
- PopBlock(previousBlock);
- return node;
+ _topLevelMethod.TryGetThisParameter(out var thisParam);
+ foreach (var closure in closures)
+ {
+ closure.CapturedEnvironments.Add(env);
+ if (thisParam != null && env.CapturedVariables.Contains(thisParam))
+ {
+ closure.CapturesThis = true;
+ }
+ }
+ });
}
- private BoundNode PushBlock(BoundNode node, ImmutableArray locals)
+ internal DebugId GetTopLevelMethodId()
{
- // blocks are not allowed in expression lambda
- Debug.Assert(!_inExpressionLambda);
-
- var previousBlock = _currentScope;
- _currentScope = node;
- if (_currentScope != previousBlock) // not top-level node of the method
- {
- // (Except for the top-level block) record the parent-child block structure
- ScopeParent[_currentScope] = previousBlock;
- }
-
- foreach (var local in locals)
- {
- VariableScope[local] = _currentScope;
- }
-
- ScopeOwner.Add(_currentScope, _currentParent);
-
- return previousBlock;
+ return _slotAllocatorOpt?.MethodId ?? new DebugId(_topLevelMethodOrdinal, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
}
- private void PopBlock(BoundNode previousBlock)
+ internal DebugId GetClosureId(SyntaxNode syntax, ArrayBuilder closureDebugInfo)
{
- _currentScope = previousBlock;
- }
+ Debug.Assert(syntax != null);
- public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
- {
- var locals = node.InnerLocals;
- if (locals.IsEmpty)
+ DebugId closureId;
+ DebugId previousClosureId;
+ if (_slotAllocatorOpt != null && _slotAllocatorOpt.TryGetPreviousClosure(syntax, out previousClosureId))
{
- // no variables declared inside the switch statement.
- return base.VisitSwitchStatement(node);
+ closureId = previousClosureId;
}
-
- var previousBlock = PushBlock(node, locals);
- var result = base.VisitSwitchStatement(node);
- PopBlock(previousBlock);
- return result;
- }
-
- public override BoundNode VisitBlock(BoundBlock node)
- {
- if (node.Locals.IsEmpty)
+ else
{
- // ignore blocks that declare no variables.
- return base.VisitBlock(node);
+ closureId = new DebugId(closureDebugInfo.Count, _compilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
}
- VisitBlockInternal(node);
- return node;
- }
+ int syntaxOffset = _topLevelMethod.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree);
+ closureDebugInfo.Add(new ClosureDebugInfo(syntaxOffset, closureId));
- private void VisitBlockInternal(BoundBlock node)
- {
- var previousBlock = PushBlock(node, node.Locals);
- base.VisitBlock(node);
- PopBlock(previousBlock);
+ return closureId;
}
- public override BoundNode VisitSequence(BoundSequence node)
+ ///
+ /// Walk up the scope tree looking for a variable declaration.
+ ///
+ public static Scope GetVariableDeclarationScope(Scope startingScope, Symbol variable)
{
- if (node.Locals.IsDefaultOrEmpty)
+ if (variable is ParameterSymbol p && p.IsThis)
{
- // ignore blocks that declare no variables.
- return base.VisitSequence(node);
+ return null;
}
- var previousBlock = PushBlock(node, node.Locals);
- var result = base.VisitSequence(node);
- PopBlock(previousBlock);
- return result;
- }
-
- public override BoundNode VisitCall(BoundCall node)
- {
- if (node.Method.MethodKind == MethodKind.LocalFunction)
+ var currentScope = startingScope;
+ while (currentScope != null)
{
- // Use OriginalDefinition to strip generic type parameters
- ReferenceVariable(node.Syntax, node.Method.OriginalDefinition);
- }
- return base.VisitCall(node);
- }
+ switch (variable.Kind)
+ {
+ case SymbolKind.Parameter:
+ case SymbolKind.Local:
+ if (currentScope.DeclaredVariables.Contains(variable))
+ {
+ return currentScope;
+ }
+ break;
- public override BoundNode VisitLambda(BoundLambda node)
- {
- MethodsConvertedToDelegates.Add(node.Symbol.OriginalDefinition);
- return VisitLambdaOrFunction(node);
- }
+ case SymbolKind.Method:
+ foreach (var closure in currentScope.Closures)
+ {
+ if (closure.OriginalMethodSymbol == variable)
+ {
+ return currentScope;
+ }
+ }
+ break;
- public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
- {
- if (node.MethodOpt?.MethodKind == MethodKind.LocalFunction)
- {
- // Use OriginalDefinition to strip generic type parameters
- ReferenceVariable(node.Syntax, node.MethodOpt.OriginalDefinition);
- MethodsConvertedToDelegates.Add(node.MethodOpt.OriginalDefinition);
+ default:
+ throw ExceptionUtilities.UnexpectedValue(variable.Kind);
+ }
+ currentScope = currentScope.Parent;
}
- return base.VisitDelegateCreationExpression(node);
+ return null;
}
- public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
+ ///
+ /// Find the parent of the corresponding to
+ /// the given .
+ ///
+ public static Scope GetScopeParent(Scope treeRoot, BoundNode scopeNode)
{
- VariableScope[node.Symbol] = _currentScope;
- return VisitLambdaOrFunction(node);
+ var correspondingScope = GetScopeWithMatchingBoundNode(treeRoot, scopeNode);
+ return correspondingScope.Parent;
}
- private BoundNode VisitLambdaOrFunction(IBoundLambdaOrFunction node)
+ ///
+ /// Finds a with a matching
+ /// as the one given.
+ ///
+ public static Scope GetScopeWithMatchingBoundNode(Scope treeRoot, BoundNode node)
{
- Debug.Assert((object)node.Symbol != null);
- SeenLambda = true;
- var oldParent = _currentParent;
- var oldBlock = _currentScope;
- _currentParent = node.Symbol;
- _currentScope = node.Body;
- ScopeParent[_currentScope] = oldBlock;
- ScopeOwner.Add(_currentScope, _currentParent);
- var wasInExpressionLambda = _inExpressionLambda;
- _inExpressionLambda = _inExpressionLambda || ((node as BoundLambda)?.Type.IsExpressionTree() ?? false);
-
- if (!_inExpressionLambda)
+ return Helper(treeRoot) ?? throw ExceptionUtilities.Unreachable;
+
+ Scope Helper(Scope currentScope)
{
- // for the purpose of constructing frames parameters are scoped as if they are inside the lambda block
- foreach (var parameter in node.Symbol.Parameters)
+ if (currentScope.BoundNode == node)
{
- VariableScope[parameter] = _currentScope;
+ return currentScope;
}
- foreach (var local in node.Body.Locals)
+ foreach (var nestedScope in currentScope.NestedScopes)
{
- VariableScope[local] = _currentScope;
+ var found = Helper(nestedScope);
+ if (found != null)
+ {
+ return found;
+ }
}
- }
-
- var result = base.VisitBlock(node.Body);
- _inExpressionLambda = wasInExpressionLambda;
- _currentParent = oldParent;
- _currentScope = oldBlock;
- return result;
- }
- private void ReferenceVariable(SyntaxNode syntax, Symbol symbol)
- {
- if (symbol is LocalSymbol localSymbol && localSymbol.IsConst)
- {
- // "constant variables" need not be captured
- return;
- }
-
- // "symbol == lambda" could happen if we're recursive
- if (_currentParent is MethodSymbol lambda && symbol != lambda && symbol.ContainingSymbol != lambda)
- {
- CapturedVariables.Add(symbol, syntax);
+ return null;
}
}
- private static bool IsClosure(MethodSymbol symbol)
+ ///
+ /// Walk up the scope tree looking for a closure.
+ ///
+ ///
+ /// A tuple of the found and the it was found in.
+ ///
+ public static (Closure, Scope) GetVisibleClosure(Scope startingScope, MethodSymbol closureSymbol)
{
- switch (symbol.MethodKind)
+ var currentScope = startingScope;
+ while (currentScope != null)
{
- case MethodKind.LambdaMethod:
- case MethodKind.LocalFunction:
- return true;
-
- default:
- return false;
+ foreach (var closure in currentScope.Closures)
+ {
+ if (closure.OriginalMethodSymbol == closureSymbol)
+ {
+ return (closure, currentScope);
+ }
+ }
+ currentScope = currentScope.Parent;
}
- }
-
- public override BoundNode VisitMethodGroup(BoundMethodGroup node)
- {
throw ExceptionUtilities.Unreachable;
}
- public override BoundNode VisitThisReference(BoundThisReference node)
+ ///
+ /// Finds a with a matching original symbol somewhere in the given scope or nested scopes.
+ ///
+ public static Closure GetClosureInTree(Scope treeRoot, MethodSymbol closureSymbol)
{
- var thisParam = _topLevelMethod.ThisParameter;
- if (thisParam != null)
- {
- ReferenceVariable(node.Syntax, thisParam);
- }
- else
- {
- // This can occur in a delegate creation expression because the method group
- // in the argument can have a "this" receiver even when "this"
- // is not captured because a static method is selected. But we do preserve
- // the method group and its receiver in the bound tree.
- // No need to capture "this" in such case.
-
- // TODO: Why don't we drop "this" while lowering if method is static?
- // Actually, considering that method group expression does not evaluate to a particular value
- // why do we have it in the lowered tree at all?
- }
+ return Helper(treeRoot) ?? throw ExceptionUtilities.Unreachable;
- return base.VisitThisReference(node);
- }
+ Closure Helper(Scope scope)
+ {
+ foreach (var closure in scope.Closures)
+ {
+ if (closure.OriginalMethodSymbol == closureSymbol)
+ {
+ return closure;
+ }
+ }
- public override BoundNode VisitBaseReference(BoundBaseReference node)
- {
- ReferenceVariable(node.Syntax, _topLevelMethod.ThisParameter);
- return base.VisitBaseReference(node);
- }
+ foreach (var nestedScope in scope.NestedScopes)
+ {
+ var found = Helper(nestedScope);
+ if (found != null)
+ {
+ return found;
+ }
+ }
- public override BoundNode VisitParameter(BoundParameter node)
- {
- ReferenceVariable(node.Syntax, node.ParameterSymbol);
- return base.VisitParameter(node);
+ return null;
+ }
}
- public override BoundNode VisitLocal(BoundLocal node)
+ public void Free()
{
- ReferenceVariable(node.Syntax, node.LocalSymbol);
- return base.VisitLocal(node);
+ MethodsConvertedToDelegates.Free();
+ ScopeTree.Free();
}
}
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.LocalFunctionReferenceRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.LocalFunctionReferenceRewriter.cs
deleted file mode 100644
index ecd59c21f1718..0000000000000
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.LocalFunctionReferenceRewriter.cs
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.CodeAnalysis.CSharp.Symbols;
-using Microsoft.CodeAnalysis.PooledObjects;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
-
-namespace Microsoft.CodeAnalysis.CSharp
-{
- internal sealed partial class LambdaRewriter : MethodToClassRewriter
- {
- ///
- /// This pass is expected to run on partially lowered methods
- /// previously containing one or more local functions. At this
- /// point all local functions should have been rewritten into
- /// proper closure classes and have frames and proxies generated
- /// for them.
- ///
- /// The only thing left is to visit all "references" to local functions
- /// and rewrite them to be references to the rewritten form.
- ///
- private sealed class LocalFunctionReferenceRewriter : BoundTreeRewriterWithStackGuard
- {
- private readonly LambdaRewriter _lambdaRewriter;
-
- public LocalFunctionReferenceRewriter(LambdaRewriter lambdaRewriter)
- {
- _lambdaRewriter = lambdaRewriter;
- }
-
- public override BoundNode Visit(BoundNode node)
- {
- var partiallyLowered = node as PartiallyLoweredLocalFunctionReference;
- if (partiallyLowered != null)
- {
- var underlying = partiallyLowered.UnderlyingNode;
- Debug.Assert(underlying.Kind == BoundKind.Call ||
- underlying.Kind == BoundKind.DelegateCreationExpression ||
- underlying.Kind == BoundKind.Conversion);
- var oldProxies = _lambdaRewriter.proxies;
- _lambdaRewriter.proxies = partiallyLowered.Proxies;
-
- var result = base.Visit(underlying);
-
- _lambdaRewriter.proxies = oldProxies;
-
- return result;
- }
- return base.Visit(node);
- }
-
- public override BoundNode VisitCall(BoundCall node)
- {
- if (node.Method.MethodKind == MethodKind.LocalFunction)
- {
- BoundExpression receiver;
- MethodSymbol method;
- var arguments = node.Arguments;
- _lambdaRewriter.RemapLocalFunction(
- node.Syntax,
- node.Method,
- out receiver,
- out method,
- ref arguments);
- node = node.Update(receiver, method, arguments);
- }
-
- return base.VisitCall(node);
- }
-
- public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
- {
- if (node.MethodOpt?.MethodKind == MethodKind.LocalFunction)
- {
- BoundExpression receiver;
- MethodSymbol method;
- var arguments = default(ImmutableArray);
- _lambdaRewriter.RemapLocalFunction(
- node.Syntax,
- node.MethodOpt,
- out receiver,
- out method,
- ref arguments);
-
- return new BoundDelegateCreationExpression(
- node.Syntax, receiver, method, isExtensionMethod: false, type: node.Type);
- }
-
- return base.VisitDelegateCreationExpression(node);
- }
- }
-
- ///
- /// Visit all references to local functions (calls, delegate
- /// conversions, delegate creations) and rewrite them to point
- /// to the rewritten local function method instead of the original.
- ///
- public BoundStatement RewriteLocalFunctionReferences(BoundStatement loweredBody)
- {
- var rewriter = new LocalFunctionReferenceRewriter(this);
-
- Debug.Assert(_currentMethod == _topLevelMethod);
-
- // Visit the body first since the state is already set
- // for the top-level method
- var newBody = (BoundStatement)rewriter.Visit(loweredBody);
-
- // Visit all the rewritten methods as well
- var synthesizedMethods = _synthesizedMethods;
- if (synthesizedMethods != null)
- {
- var newMethods = ArrayBuilder.GetInstance(
- synthesizedMethods.Count);
-
- foreach (var oldMethod in synthesizedMethods)
- {
- var synthesizedLambda = oldMethod.Method as SynthesizedLambdaMethod;
- if (synthesizedLambda == null)
- {
- // The only methods synthesized by the rewriter should
- // be lowered closures and frame constructors
- Debug.Assert(oldMethod.Method.MethodKind == MethodKind.Constructor ||
- oldMethod.Method.MethodKind == MethodKind.StaticConstructor);
- newMethods.Add(oldMethod);
- continue;
- }
-
- _currentMethod = synthesizedLambda;
- var closureKind = synthesizedLambda.ClosureKind;
- if (closureKind == ClosureKind.Static || closureKind == ClosureKind.Singleton)
- {
- // no link from a static lambda to its container
- _innermostFramePointer = _currentFrameThis = null;
- }
- else
- {
- _currentFrameThis = synthesizedLambda.ThisParameter;
- _innermostFramePointer = null;
- _framePointers.TryGetValue(synthesizedLambda.ContainingType, out _innermostFramePointer);
- }
-
- var containerAsFrame = synthesizedLambda.ContainingType as LambdaFrame;
-
- // Includes type parameters from the containing type iff
- // the containing type is a frame. If it is a frame then
- // the type parameters are captured, meaning that the
- // type parameters should be included.
- // If it is not a frame then the local function is being
- // directly lowered into the method's containing type and
- // the parameters should never be substituted.
- _currentTypeParameters = containerAsFrame?.TypeParameters.Concat(synthesizedLambda.TypeParameters)
- ?? synthesizedLambda.TypeParameters;
- _currentLambdaBodyTypeMap = synthesizedLambda.TypeMap;
-
- var rewrittenBody = (BoundStatement)rewriter.Visit(oldMethod.Body);
-
- var newMethod = new TypeCompilationState.MethodWithBody(
- synthesizedLambda, rewrittenBody, oldMethod.ImportChainOpt);
- newMethods.Add(newMethod);
- }
-
- _synthesizedMethods = newMethods;
- synthesizedMethods.Free();
- }
-
- return newBody;
- }
-
- ///
- /// Rewrites a reference to an unlowered local function to the newly
- /// lowered local function.
- ///
- private void RemapLocalFunction(
- SyntaxNode syntax,
- MethodSymbol localFunc,
- out BoundExpression receiver,
- out MethodSymbol method,
- ref ImmutableArray parameters)
- {
- Debug.Assert(localFunc.MethodKind == MethodKind.LocalFunction);
-
- var mappedLocalFunction = _localFunctionMap[(LocalFunctionSymbol)localFunc.OriginalDefinition];
- var loweredSymbol = mappedLocalFunction.Symbol;
-
- // If the local function captured variables then they will be stored
- // in frames and the frames need to be passed as extra parameters.
- var frameCount = loweredSymbol.ExtraSynthesizedParameterCount;
- if (frameCount != 0)
- {
- Debug.Assert(!parameters.IsDefault);
-
- // Build a new list of parameters to pass to the local function
- // call that includes any necessary capture frames
- var parametersBuilder = ArrayBuilder.GetInstance();
- parametersBuilder.AddRange(parameters);
-
- var start = loweredSymbol.ParameterCount - frameCount;
- for (int i = start; i < loweredSymbol.ParameterCount; i++)
- {
- // will always be a LambdaFrame, it's always a capture frame
- var frameType = (NamedTypeSymbol)loweredSymbol.Parameters[i].Type.OriginalDefinition;
-
- Debug.Assert(frameType is LambdaFrame);
-
- if (frameType.Arity > 0)
- {
- var typeParameters = ((LambdaFrame)frameType).ConstructedFromTypeParameters;
- Debug.Assert(typeParameters.Length == frameType.Arity);
- var subst = this.TypeMap.SubstituteTypeParameters(typeParameters);
- frameType = frameType.Construct(subst);
- }
-
- var frame = FrameOfType(syntax, frameType);
- parametersBuilder.Add(frame);
- }
- parameters = parametersBuilder.ToImmutableAndFree();
- }
-
- method = loweredSymbol;
- NamedTypeSymbol constructedFrame;
-
- RemapLambdaOrLocalFunction(syntax,
- localFunc,
- SubstituteTypeArguments(localFunc.TypeArguments),
- mappedLocalFunction.ClosureKind,
- ref method,
- out receiver,
- out constructedFrame);
- }
-
- ///
- /// Substitutes references from old type arguments to new type arguments
- /// in the lowered methods.
- ///
- ///
- /// Consider the following method:
- /// void M() {
- /// void L<T>(T t) => Console.Write(t);
- /// L("A");
- /// }
- ///
- /// In this example, L<T> is a local function that will be
- /// lowered into its own method and the type parameter T will be
- /// alpha renamed to something else (let's call it T'). In this case,
- /// all references to the original type parameter T in L must be
- /// rewritten to the renamed parameter, T'.
- ///
- private ImmutableArray SubstituteTypeArguments(ImmutableArray typeArguments)
- {
- Debug.Assert(!typeArguments.IsDefault);
-
- if (typeArguments.IsEmpty)
- {
- return typeArguments;
- }
-
- // We must perform this process repeatedly as local
- // functions may nest inside one another and capture type
- // parameters from the enclosing local functions. Each
- // iteration of nesting will cause alpha-renaming of the captured
- // parameters, meaning that we must replace until there are no
- // more alpha-rename mappings.
- //
- // The method symbol references are different from all other
- // substituted types in this context because the method symbol in
- // local function references is not rewritten until all local
- // functions have already been lowered. Everything else is rewritten
- // by the visitors as the definition is lowered. This means that
- // only one substition happens per lowering, but we need to do
- // N substitutions all at once, where N is the number of lowerings.
-
- var builder = ArrayBuilder.GetInstance();
- foreach (var typeArg in typeArguments)
- {
- TypeSymbol oldTypeArg;
- TypeSymbol newTypeArg = typeArg;
- do
- {
- oldTypeArg = newTypeArg;
- newTypeArg = this.TypeMap.SubstituteType(typeArg).Type;
- }
- while (oldTypeArg != newTypeArg);
-
- Debug.Assert((object)oldTypeArg == newTypeArg);
-
- builder.Add(newTypeArg);
- }
-
- return builder.ToImmutableAndFree();
- }
- }
-}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs
index 6228f785c567a..4c43b74d4a1ad 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaRewriter.cs
@@ -30,7 +30,7 @@ namespace Microsoft.CodeAnalysis.CSharp
/// have captured variables. The result of this analysis is left in .
///
/// Then we make a frame, or compiler-generated class, represented by an instance of
- /// for each scope with captured variables. The generated frames are kept
+ /// for each scope with captured variables. The generated frames are kept
/// in . Each frame is given a single field for each captured
/// variable in the corresponding scope. These are maintained in .
///
@@ -73,27 +73,13 @@ internal sealed partial class LambdaRewriter : MethodToClassRewriter
// lambda frame for static lambdas.
// initialized lazily and could be null if there are no static lambdas
- private LambdaFrame _lazyStaticLambdaFrame;
+ private SynthesizedClosureEnvironment _lazyStaticLambdaFrame;
// A mapping from every lambda parameter to its corresponding method's parameter.
private readonly Dictionary _parameterMap = new Dictionary();
- // A mapping from every local function to its lowered method
- private struct MappedLocalFunction
- {
- public readonly SynthesizedLambdaMethod Symbol;
- public readonly ClosureKind ClosureKind;
- public MappedLocalFunction(SynthesizedLambdaMethod symbol, ClosureKind closureKind)
- {
- Symbol = symbol;
- ClosureKind = closureKind;
- }
- }
-
- private readonly Dictionary _localFunctionMap = new Dictionary();
-
// for each block with lifted (captured) variables, the corresponding frame type
- private readonly Dictionary _frames = new Dictionary();
+ private readonly Dictionary _frames = new Dictionary();
// the current set of frame pointers in scope. Each is either a local variable (where introduced),
// or the "this" parameter when at the top level. Keys in this map are never constructed types.
@@ -149,6 +135,15 @@ public MappedLocalFunction(SynthesizedLambdaMethod symbol, ClosureKind closureKi
///
private ArrayBuilder _synthesizedMethods;
+ ///
+ /// TODO(https://github.com/dotnet/roslyn/projects/26): Delete this.
+ /// This should only be used by which
+ /// hasn't had logic to move the proxy analysis into ,
+ /// where the could be walked to build
+ /// the proxy list.
+ ///
+ private readonly ImmutableHashSet _allCapturedVariables;
+
private LambdaRewriter(
Analysis analysis,
NamedTypeSymbol thisType,
@@ -182,13 +177,20 @@ private LambdaRewriter(
_framePointers[thisType] = thisParameterOpt;
_seenBaseCall = method.MethodKind != MethodKind.Constructor; // only used for ctors
_synthesizedFieldNameIdDispenser = 1;
+
+ var allCapturedVars = ImmutableHashSet.CreateBuilder();
+ Analysis.VisitClosures(analysis.ScopeTree, (scope, closure) =>
+ {
+ allCapturedVars.UnionWith(closure.CapturedVariables);
+ });
+ _allCapturedVariables = allCapturedVars.ToImmutable();
}
protected override bool NeedsProxy(Symbol localOrParameter)
{
Debug.Assert(localOrParameter is LocalSymbol || localOrParameter is ParameterSymbol ||
(localOrParameter as MethodSymbol)?.MethodKind == MethodKind.LocalFunction);
- return _analysis.CapturedVariables.ContainsKey(localOrParameter);
+ return _allCapturedVariables.Contains(localOrParameter);
}
///
@@ -227,16 +229,15 @@ public static BoundStatement Rewrite(
Debug.Assert(((object)thisParameter == null) || (thisParameter.Type == thisType));
Debug.Assert(compilationState.ModuleBuilderOpt != null);
- var analysis = Analysis.Analyze(loweredBody, method);
- if (!analysis.SeenLambda)
- {
- // Unreachable anonymous functions are ignored by the analyzer.
- // No closures or lambda methods are generated.
- // E.g.
- // int y = 0;
- // var b = false && (from z in new X(y) select f(z + y))
- return loweredBody;
- }
+ var analysis = Analysis.Analyze(
+ loweredBody,
+ method,
+ methodOrdinal,
+ substitutedSourceMethod,
+ slotAllocatorOpt,
+ compilationState,
+ closureDebugInfoBuilder,
+ diagnostics);
CheckLocalsDefined(loweredBody);
var rewriter = new LambdaRewriter(
@@ -252,20 +253,12 @@ public static BoundStatement Rewrite(
diagnostics,
assignLocals);
- analysis.ComputeLambdaScopesAndFrameCaptures();
- rewriter.MakeFrames(closureDebugInfoBuilder);
+ rewriter.SynthesizeClosureEnvironments(closureDebugInfoBuilder);
+ rewriter.SynthesizeLoweredFunctionMethods();
- // First, lower everything but references (calls, delegate conversions)
- // to local functions
var body = rewriter.AddStatementsIfNeeded(
(BoundStatement)rewriter.Visit(loweredBody));
- // Now lower the references
- if (rewriter._localFunctionMap.Count != 0)
- {
- body = rewriter.RewriteLocalFunctionReferences(body);
- }
-
// Add the completed methods to the compilation state
if (rewriter._synthesizedMethods != null)
{
@@ -282,6 +275,8 @@ public static BoundStatement Rewrite(
CheckLocalsDefined(body);
+ analysis.Free();
+
return body;
}
@@ -324,168 +319,158 @@ protected override NamedTypeSymbol ContainingType
static partial void CheckLocalsDefined(BoundNode node);
///
- /// Create the frame types.
+ /// Adds synthesized types to the compilation state
+ /// and creates hoisted fields for all locals captured by the environments.
///
- private void MakeFrames(ArrayBuilder closureDebugInfo)
+ private void SynthesizeClosureEnvironments(ArrayBuilder closureDebugInfo)
{
- Analysis.VisitClosures(_analysis.ScopeTree, (scope, closure) =>
+ Analysis.VisitScopeTree(_analysis.ScopeTree, scope =>
{
- var capturedVars = closure.CapturedVariables;
- MethodSymbol closureSymbol = closure.OriginalMethodSymbol;
- bool canTakeRefParams = _analysis.CanTakeRefParameters(closureSymbol);
-
- if (canTakeRefParams && OnlyCapturesThis(closure, scope))
+ if (scope.DeclaredEnvironments.Count > 0)
{
- return;
- }
+ Debug.Assert(!_frames.ContainsKey(scope.BoundNode));
+ // At the moment, all variables declared in the same
+ // scope always get assigned to the same environment
+ Debug.Assert(scope.DeclaredEnvironments.Count == 1);
- foreach (var captured in capturedVars)
- {
- if (!_analysis.VariableScope.TryGetValue(captured, out BoundNode captureScope))
- {
- continue;
- }
+ var env = scope.DeclaredEnvironments[0];
+ var frame = MakeFrame(scope, env.IsStruct);
+ env.SynthesizedEnvironment = frame;
- // If this is a local function that can take ref params, skip
- // frame creation for local function calls. This is semantically
- // important because otherwise we may create a struct frame which
- // is empty, which crashes in emit.
- // This is not valid for lambdas or local functions which can't take
- // take ref params since they will be lowered into their own frames.
- if (canTakeRefParams && captured.Kind == SymbolKind.Method)
+ CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(ContainingType, frame);
+ if (frame.Constructor != null)
{
- continue;
+ AddSynthesizedMethod(
+ frame.Constructor,
+ FlowAnalysisPass.AppendImplicitReturn(
+ MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, null),
+ frame.Constructor));
}
- LambdaFrame frame = GetFrameForScope(captureScope, closureDebugInfo);
-
- if (captured.Kind != SymbolKind.Method && !proxies.ContainsKey(captured))
+ foreach (var captured in env.CapturedVariables)
{
+ Debug.Assert(!proxies.ContainsKey(captured));
+
var hoistedField = LambdaCapturedVariable.Create(frame, captured, ref _synthesizedFieldNameIdDispenser);
proxies.Add(captured, new CapturedToFrameSymbolReplacement(hoistedField, isReusable: false));
CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(frame, hoistedField);
-
- if (hoistedField.Type.IsRestrictedType())
- {
- foreach (CSharpSyntaxNode syntax in _analysis.CapturedVariables[captured])
- {
- // CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method
- this.Diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, hoistedField.Type);
- }
- }
}
+
+ _frames.Add(scope.BoundNode, env);
}
});
- }
- private SmallDictionary _onlyCapturesThisMemoTable;
- ///
- /// Helper for determining whether a local function transitively
- /// only captures this (only captures this or other local functions
- /// which only capture this).
- ///
- private bool OnlyCapturesThis(
- Analysis.Closure closure,
- Analysis.Scope scope,
- PooledHashSet localFuncsInProgress = null)
- {
- Debug.Assert(closure != null);
- Debug.Assert(scope != null);
-
- bool result = false;
- if (_onlyCapturesThisMemoTable?.TryGetValue(closure, out result) == true)
+ SynthesizedClosureEnvironment MakeFrame(Analysis.Scope scope, bool isStruct)
{
- return result;
- }
-
- result = true;
- foreach (var captured in closure.CapturedVariables)
- {
- var param = captured as ParameterSymbol;
- if (param != null && param.IsThis)
- {
- continue;
- }
+ var scopeBoundNode = scope.BoundNode;
- var localFunc = captured as LocalFunctionSymbol;
- if (localFunc != null)
- {
- bool freePool = false;
- if (localFuncsInProgress == null)
- {
- localFuncsInProgress = PooledHashSet.GetInstance();
- freePool = true;
- }
- else if (localFuncsInProgress.Contains(localFunc))
- {
- continue;
- }
+ var syntax = scopeBoundNode.Syntax;
+ Debug.Assert(syntax != null);
- localFuncsInProgress.Add(localFunc);
- var (found, foundScope) = Analysis.GetVisibleClosure(scope, localFunc);
- bool transitivelyTrue = OnlyCapturesThis(found, foundScope, localFuncsInProgress);
+ DebugId methodId = _analysis.GetTopLevelMethodId();
+ DebugId closureId = _analysis.GetClosureId(syntax, closureDebugInfo);
- if (freePool)
- {
- localFuncsInProgress.Free();
- localFuncsInProgress = null;
- }
-
- if (transitivelyTrue)
- {
- continue;
- }
+ var containingMethod = scope.ContainingClosureOpt?.OriginalMethodSymbol ?? _topLevelMethod;
+ if ((object)_substitutedSourceMethod != null && containingMethod == _topLevelMethod)
+ {
+ containingMethod = _substitutedSourceMethod;
}
- result = false;
- break;
+ return new SynthesizedClosureEnvironment(
+ _topLevelMethod,
+ containingMethod,
+ isStruct,
+ syntax,
+ methodId,
+ closureId);
}
-
- if (_onlyCapturesThisMemoTable == null)
- {
- _onlyCapturesThisMemoTable = new SmallDictionary();
- }
-
- _onlyCapturesThisMemoTable[closure] = result;
- return result;
}
- private LambdaFrame GetFrameForScope(BoundNode scope, ArrayBuilder closureDebugInfo)
+ ///
+ /// Synthesize the final signature for all closures.
+ ///
+ private void SynthesizeLoweredFunctionMethods()
{
- LambdaFrame frame;
- if (!_frames.TryGetValue(scope, out frame))
+ Analysis.VisitClosures(_analysis.ScopeTree, (scope, closure) =>
{
- var syntax = scope.Syntax;
- Debug.Assert(syntax != null);
-
- DebugId methodId = GetTopLevelMethodId();
- DebugId closureId = GetClosureId(syntax, closureDebugInfo);
-
- var canBeStruct = !_analysis.ScopesThatCantBeStructs.Contains(scope);
+ var originalMethod = closure.OriginalMethodSymbol;
+ var syntax = originalMethod.DeclaringSyntaxReferences[0].GetSyntax();
+ var structClosures = closure.CapturedEnvironments
+ .Where(env => env.IsStruct).Select(env => env.SynthesizedEnvironment).AsImmutable();
+
+ int closureOrdinal;
+ ClosureKind closureKind;
+ NamedTypeSymbol translatedLambdaContainer;
+ SynthesizedClosureEnvironment containerAsFrame;
+ DebugId topLevelMethodId;
+ DebugId lambdaId;
+ if (closure.ContainingEnvironmentOpt != null)
+ {
+ containerAsFrame = closure.ContainingEnvironmentOpt?.SynthesizedEnvironment;
- var containingMethod = _analysis.ScopeOwner[scope];
- if (_substitutedSourceMethod != null && containingMethod == _topLevelMethod)
+ closureKind = ClosureKind.General;
+ translatedLambdaContainer = containerAsFrame;
+ closureOrdinal = containerAsFrame.ClosureOrdinal;
+ }
+ else if (closure.CapturesThis)
{
- containingMethod = _substitutedSourceMethod;
+ containerAsFrame = null;
+ translatedLambdaContainer = _topLevelMethod.ContainingType;
+ closureKind = ClosureKind.ThisOnly;
+ closureOrdinal = LambdaDebugInfo.ThisOnlyClosureOrdinal;
}
- frame = new LambdaFrame(_topLevelMethod, containingMethod, canBeStruct, syntax, methodId, closureId);
- _frames.Add(scope, frame);
-
- CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(ContainingType, frame);
- if (frame.Constructor != null)
+ else if (closure.CapturedEnvironments.Count == 0)
{
- AddSynthesizedMethod(
- frame.Constructor,
- FlowAnalysisPass.AppendImplicitReturn(
- MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, null),
- frame.Constructor));
+ if (_analysis.MethodsConvertedToDelegates.Contains(originalMethod))
+ {
+ translatedLambdaContainer = containerAsFrame = GetStaticFrame(Diagnostics, syntax);
+ closureKind = ClosureKind.Singleton;
+ closureOrdinal = LambdaDebugInfo.StaticClosureOrdinal;
+ }
+ else
+ {
+ containerAsFrame = null;
+ translatedLambdaContainer = _topLevelMethod.ContainingType;
+ closureKind = ClosureKind.Static;
+ closureOrdinal = LambdaDebugInfo.StaticClosureOrdinal;
+ }
+ }
+ else
+ {
+ // Lower directly onto the containing type
+ containerAsFrame = null;
+ closureKind = ClosureKind.Static; // not exactly... but we've rewritten the receiver to be a by-ref parameter
+ translatedLambdaContainer = _topLevelMethod.ContainingType;
+ closureOrdinal = LambdaDebugInfo.StaticClosureOrdinal;
}
- }
- return frame;
+ // Move the body of the lambda to a freshly generated synthetic method on its frame.
+ topLevelMethodId = _analysis.GetTopLevelMethodId();
+ lambdaId = GetLambdaId(syntax, closureKind, closureOrdinal);
+
+ var synthesizedMethod = new SynthesizedClosureMethod(
+ translatedLambdaContainer,
+ structClosures,
+ closureKind,
+ _topLevelMethod,
+ topLevelMethodId,
+ originalMethod,
+ closure.BlockSyntax,
+ lambdaId);
+ closure.SynthesizedLoweredMethod = synthesizedMethod;
+ });
}
- private LambdaFrame GetStaticFrame(DiagnosticBag diagnostics, IBoundLambdaOrFunction lambda)
+ ///
+ /// Get the static container for closures or create one if one doesn't already exist.
+ ///
+ ///
+ /// associate the frame with the first lambda that caused it to exist.
+ /// we need to associate this with some syntax.
+ /// unfortunately either containing method or containing class could be synthetic
+ /// therefore could have no syntax.
+ ///
+ private SynthesizedClosureEnvironment GetStaticFrame(DiagnosticBag diagnostics, SyntaxNode syntax)
{
if (_lazyStaticLambdaFrame == null)
{
@@ -504,13 +489,19 @@ private LambdaFrame GetStaticFrame(DiagnosticBag diagnostics, IBoundLambdaOrFunc
}
else
{
- methodId = GetTopLevelMethodId();
+ methodId = _analysis.GetTopLevelMethodId();
}
DebugId closureId = default(DebugId);
// using _topLevelMethod as containing member because the static frame does not have generic parameters, except for the top level method's
var containingMethod = isNonGeneric ? null : (_substitutedSourceMethod ?? _topLevelMethod);
- _lazyStaticLambdaFrame = new LambdaFrame(_topLevelMethod, containingMethod, isStruct: false, scopeSyntaxOpt: null, methodId: methodId, closureId: closureId);
+ _lazyStaticLambdaFrame = new SynthesizedClosureEnvironment(
+ _topLevelMethod,
+ containingMethod,
+ isStruct: false,
+ scopeSyntaxOpt: null,
+ methodId: methodId,
+ closureId: closureId);
// non-generic static lambdas can share the frame
if (isNonGeneric)
@@ -530,12 +521,6 @@ private LambdaFrame GetStaticFrame(DiagnosticBag diagnostics, IBoundLambdaOrFunc
MethodCompiler.BindMethodBody(frame.Constructor, CompilationState, null),
frame.Constructor));
- // associate the frame with the first lambda that caused it to exist.
- // we need to associate this with some syntax.
- // unfortunately either containing method or containing class could be synthetic
- // therefore could have no syntax.
- SyntaxNode syntax = lambda.Syntax;
-
// add cctor
// Frame.inst = new Frame()
var F = new SyntheticBoundNodeFactory(frame.StaticConstructor, syntax, CompilationState, diagnostics);
@@ -584,7 +569,7 @@ protected override BoundExpression FramePointer(SyntaxNode syntax, NamedTypeSymb
}
// If the current method has by-ref struct closure parameters, and one of them is correct, use it.
- var lambda = _currentMethod as SynthesizedLambdaMethod;
+ var lambda = _currentMethod as SynthesizedClosureMethod;
if (lambda != null)
{
var start = lambda.ParameterCount - lambda.ExtraSynthesizedParameterCount;
@@ -627,11 +612,12 @@ private static void InsertAndFreePrologue(ArrayBuilder result, A
/// Introduce a frame around the translation of the given node.
///
/// The node whose translation should be translated to contain a frame
- /// The frame for the translated node
+ /// The environment for the translated node
/// A function that computes the translation of the node. It receives lists of added statements and added symbols
/// The translated statement, as returned from F
- private BoundNode IntroduceFrame(BoundNode node, LambdaFrame frame, Func, ArrayBuilder, BoundNode> F)
+ private BoundNode IntroduceFrame(BoundNode node, Analysis.ClosureEnvironment env, Func, ArrayBuilder, BoundNode> F)
{
+ var frame = env.SynthesizedEnvironment;
var frameTypeParameters = ImmutableArray.Create(StaticCast.From(_currentTypeParameters).SelectAsArray(TypeMap.TypeSymbolAsTypeWithModifiers), 0, frame.Arity);
NamedTypeSymbol frameType = frame.ConstructIfGeneric(frameTypeParameters);
@@ -644,7 +630,7 @@ private BoundNode IntroduceFrame(BoundNode node, LambdaFrame frame, Func.GetInstance();
- if (frame.Constructor != null)
+ if ((object)frame.Constructor != null)
{
MethodSymbol constructor = frame.Constructor.AsMember(frameType);
Debug.Assert(frameType == constructor.ContainingType);
@@ -659,34 +645,14 @@ private BoundNode IntroduceFrame(BoundNode node, LambdaFrame frame, Func.GetInstance();
addedLocals.Add(framePointer);
_framePointers.Add(frame, framePointer);
@@ -778,7 +740,20 @@ private void InitVariableProxy(SyntaxNode syntax, Symbol symbol, LocalSymbol fra
var left = proxy.Replacement(syntax, frameType1 => new BoundLocal(syntax, framePointer, null, framePointer.Type));
var assignToProxy = new BoundAssignmentOperator(syntax, left, value, value.Type);
- prologue.Add(assignToProxy);
+ if (_currentMethod.MethodKind == MethodKind.Constructor &&
+ symbol == _currentMethod.ThisParameter &&
+ !_seenBaseCall)
+ {
+ // Containing method is a constructor
+ // Initialization statement for the "this" proxy must be inserted
+ // after the constructor initializer statement block
+ Debug.Assert(_thisProxyInitDeferred == null);
+ _thisProxyInitDeferred = assignToProxy;
+ }
+ else
+ {
+ prologue.Add(assignToProxy);
+ }
}
}
@@ -819,6 +794,129 @@ public override BoundNode VisitBaseReference(BoundBaseReference node)
: FramePointer(node.Syntax, _topLevelMethod.ContainingType); // technically, not the correct static type
}
+ ///
+ /// Rewrites a reference to an unlowered local function to the newly
+ /// lowered local function.
+ ///
+ private void RemapLocalFunction(
+ SyntaxNode syntax,
+ MethodSymbol localFunc,
+ out BoundExpression receiver,
+ out MethodSymbol method,
+ ref ImmutableArray parameters)
+ {
+ Debug.Assert(localFunc.MethodKind == MethodKind.LocalFunction);
+
+ var closure = Analysis.GetClosureInTree(_analysis.ScopeTree, localFunc.OriginalDefinition);
+ var loweredSymbol = closure.SynthesizedLoweredMethod;
+
+ // If the local function captured variables then they will be stored
+ // in frames and the frames need to be passed as extra parameters.
+ var frameCount = loweredSymbol.ExtraSynthesizedParameterCount;
+ if (frameCount != 0)
+ {
+ Debug.Assert(!parameters.IsDefault);
+
+ // Build a new list of parameters to pass to the local function
+ // call that includes any necessary capture frames
+ var parametersBuilder = ArrayBuilder.GetInstance();
+ parametersBuilder.AddRange(parameters);
+
+ var start = loweredSymbol.ParameterCount - frameCount;
+ for (int i = start; i < loweredSymbol.ParameterCount; i++)
+ {
+ // will always be a LambdaFrame, it's always a capture frame
+ var frameType = (NamedTypeSymbol)loweredSymbol.Parameters[i].Type.OriginalDefinition;
+
+ Debug.Assert(frameType is SynthesizedClosureEnvironment);
+
+ if (frameType.Arity > 0)
+ {
+ var typeParameters = ((SynthesizedClosureEnvironment)frameType).ConstructedFromTypeParameters;
+ Debug.Assert(typeParameters.Length == frameType.Arity);
+ var subst = this.TypeMap.SubstituteTypeParameters(typeParameters);
+ frameType = frameType.Construct(subst);
+ }
+
+ var frame = FrameOfType(syntax, frameType);
+ parametersBuilder.Add(frame);
+ }
+ parameters = parametersBuilder.ToImmutableAndFree();
+ }
+
+ method = loweredSymbol;
+ NamedTypeSymbol constructedFrame;
+
+ RemapLambdaOrLocalFunction(syntax,
+ localFunc,
+ SubstituteTypeArguments(localFunc.TypeArguments),
+ loweredSymbol.ClosureKind,
+ ref method,
+ out receiver,
+ out constructedFrame);
+ }
+
+ ///
+ /// Substitutes references from old type arguments to new type arguments
+ /// in the lowered methods.
+ ///
+ ///
+ /// Consider the following method:
+ /// void M() {
+ /// void L<T>(T t) => Console.Write(t);
+ /// L("A");
+ /// }
+ ///
+ /// In this example, L<T> is a local function that will be
+ /// lowered into its own method and the type parameter T will be
+ /// alpha renamed to something else (let's call it T'). In this case,
+ /// all references to the original type parameter T in L must be
+ /// rewritten to the renamed parameter, T'.
+ ///
+ private ImmutableArray SubstituteTypeArguments(ImmutableArray typeArguments)
+ {
+ Debug.Assert(!typeArguments.IsDefault);
+
+ if (typeArguments.IsEmpty)
+ {
+ return typeArguments;
+ }
+
+ // We must perform this process repeatedly as local
+ // functions may nest inside one another and capture type
+ // parameters from the enclosing local functions. Each
+ // iteration of nesting will cause alpha-renaming of the captured
+ // parameters, meaning that we must replace until there are no
+ // more alpha-rename mappings.
+ //
+ // The method symbol references are different from all other
+ // substituted types in this context because the method symbol in
+ // local function references is not rewritten until all local
+ // functions have already been lowered. Everything else is rewritten
+ // by the visitors as the definition is lowered. This means that
+ // only one substition happens per lowering, but we need to do
+ // N substitutions all at once, where N is the number of lowerings.
+
+ var builder = ArrayBuilder.GetInstance();
+ foreach (var typeArg in typeArguments)
+ {
+ TypeSymbol oldTypeArg;
+ TypeSymbol newTypeArg = typeArg;
+ do
+ {
+ oldTypeArg = newTypeArg;
+ newTypeArg = this.TypeMap.SubstituteType(typeArg).Type;
+ }
+ while (oldTypeArg != newTypeArg);
+
+ Debug.Assert((object)oldTypeArg == newTypeArg);
+
+ builder.Add(newTypeArg);
+ }
+
+ return builder.ToImmutableAndFree();
+ }
+
private void RemapLambdaOrLocalFunction(
SyntaxNode syntax,
MethodSymbol originalMethod,
@@ -829,7 +927,7 @@ private void RemapLambdaOrLocalFunction(
out NamedTypeSymbol constructedFrame)
{
var translatedLambdaContainer = synthesizedMethod.ContainingType;
- var containerAsFrame = translatedLambdaContainer as LambdaFrame;
+ var containerAsFrame = translatedLambdaContainer as SynthesizedClosureEnvironment;
// All of _currentTypeParameters might not be preserved here due to recursively calling upwards in the chain of local functions/lambdas
Debug.Assert((typeArgumentsOpt.IsDefault && !originalMethod.IsGenericMethod) || (typeArgumentsOpt.Length == originalMethod.Arity));
@@ -878,22 +976,24 @@ private void RemapLambdaOrLocalFunction(
}
}
- ///
- /// This pass doesn't rewrite the local function calls themselves
- /// because we may encounter a call to a local function that has yet
- /// to be lowered. Here we just want to make sure we lower the
- /// arguments as they may contain references to captured variables.
- /// The final lowering of the call will be in the
- ///
- ///
public override BoundNode VisitCall(BoundCall node)
{
if (node.Method.MethodKind == MethodKind.LocalFunction)
{
- var withArguments = node.Update(
- node.ReceiverOpt,
+ var args = VisitList(node.Arguments);
+ var type = VisitType(node.Type);
+
+ RemapLocalFunction(
+ node.Syntax,
node.Method,
- this.VisitList(node.Arguments),
+ out var receiver,
+ out var method,
+ ref args);
+
+ return node.Update(
+ receiver,
+ method,
+ args,
node.ArgumentNamesOpt,
node.ArgumentRefKindsOpt,
node.IsDelegateCall,
@@ -902,9 +1002,7 @@ public override BoundNode VisitCall(BoundCall node)
node.ArgsToParamsOpt,
node.ResultKind,
node.BinderOpt,
- this.VisitType(node.Type));
-
- return PartiallyLowerLocalFunctionReference(withArguments);
+ type);
}
var visited = base.VisitCall(node);
@@ -918,34 +1016,26 @@ public override BoundNode VisitCall(BoundCall node)
// Check if we need to init the 'this' proxy in a ctor call
if (!_seenBaseCall)
{
- _seenBaseCall = _currentMethod == _topLevelMethod && node.IsConstructorInitializer();
- if (_seenBaseCall && _thisProxyInitDeferred != null)
+ if (_currentMethod == _topLevelMethod && node.IsConstructorInitializer())
{
- // Insert the this proxy assignment after the ctor call.
- // Create bound sequence: { ctor call, thisProxyInitDeferred }
- return new BoundSequence(
- syntax: node.Syntax,
- locals: ImmutableArray.Empty,
- sideEffects: ImmutableArray.Create(rewritten),
- value: _thisProxyInitDeferred,
- type: rewritten.Type);
+ _seenBaseCall = true;
+ if (_thisProxyInitDeferred != null)
+ {
+ // Insert the this proxy assignment after the ctor call.
+ // Create bound sequence: { ctor call, thisProxyInitDeferred }
+ return new BoundSequence(
+ syntax: node.Syntax,
+ locals: ImmutableArray.Empty,
+ sideEffects: ImmutableArray.Create(rewritten),
+ value: _thisProxyInitDeferred,
+ type: rewritten.Type);
+ }
}
}
return rewritten;
}
- private PartiallyLoweredLocalFunctionReference PartiallyLowerLocalFunctionReference(
- BoundExpression underlyingNode)
- {
- Debug.Assert(underlyingNode.Kind == BoundKind.Call ||
- underlyingNode.Kind == BoundKind.DelegateCreationExpression ||
- underlyingNode.Kind == BoundKind.Conversion);
- return new PartiallyLoweredLocalFunctionReference(
- underlyingNode,
- new Dictionary(proxies));
- }
-
private BoundSequence RewriteSequence(BoundSequence node, ArrayBuilder prologue, ArrayBuilder newLocals)
{
RewriteLocals(node.Locals, newLocals);
@@ -964,9 +1054,8 @@ private BoundSequence RewriteSequence(BoundSequence node, ArrayBuilder prologue, ArrayBuilder newLocals) =>
RewriteBlock(node, prologue, newLocals));
@@ -1022,8 +1111,7 @@ public override BoundNode VisitScope(BoundScope node)
public override BoundNode VisitCatchBlock(BoundCatchBlock node)
{
// Test if this frame has captured variables and requires the introduction of a closure class.
- LambdaFrame frame;
- if (_frames.TryGetValue(node, out frame))
+ if (_frames.TryGetValue(node, out var frame))
{
return IntroduceFrame(node, frame, (ArrayBuilder prologue, ArrayBuilder newLocals) =>
{
@@ -1089,9 +1177,8 @@ private BoundNode RewriteCatch(BoundCatchBlock node, ArrayBuilder prologue, ArrayBuilder newLocals) =>
{
@@ -1106,10 +1193,9 @@ public override BoundNode VisitSequence(BoundSequence node)
public override BoundNode VisitStatementList(BoundStatementList node)
{
- LambdaFrame frame;
// Test if this frame has captured variables and requires the introduction of a closure class.
// That can occur for a BoundStatementList if it is the body of a method with captured parameters.
- if (_frames.TryGetValue(node, out frame))
+ if (_frames.TryGetValue(node, out var frame))
{
return IntroduceFrame(node, frame, (ArrayBuilder prologue, ArrayBuilder newLocals) =>
{
@@ -1132,9 +1218,8 @@ public override BoundNode VisitStatementList(BoundStatementList node)
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
{
- LambdaFrame frame;
// Test if this frame has captured variables and requires the introduction of a closure class.
- if (_frames.TryGetValue(node, out frame))
+ if (_frames.TryGetValue(node, out var frame))
{
return IntroduceFrame(node, frame, (ArrayBuilder prologue, ArrayBuilder newLocals) =>
{
@@ -1162,13 +1247,20 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE
if (node.MethodOpt?.MethodKind == MethodKind.LocalFunction)
{
- var rewritten = node.Update(
- node.Argument,
+ var arguments = default(ImmutableArray);
+ RemapLocalFunction(
+ node.Syntax,
node.MethodOpt,
- node.IsExtensionMethod,
- this.VisitType(node.Type));
+ out var receiver,
+ out var method,
+ ref arguments);
- return PartiallyLowerLocalFunctionReference(rewritten);
+ return new BoundDelegateCreationExpression(
+ node.Syntax,
+ receiver,
+ method,
+ node.IsExtensionMethod,
+ VisitType(node.Type));
}
return base.VisitDelegateCreationExpression(node);
}
@@ -1203,7 +1295,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
{
ClosureKind closureKind;
NamedTypeSymbol translatedLambdaContainer;
- LambdaFrame containerAsFrame;
+ SynthesizedClosureEnvironment containerAsFrame;
BoundNode lambdaScope;
DebugId topLevelMethodId;
DebugId lambdaId;
@@ -1219,32 +1311,6 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
return new BoundNoOpStatement(node.Syntax, NoOpStatementFlavor.Default);
}
- private DebugId GetTopLevelMethodId()
- {
- return slotAllocatorOpt?.MethodId ?? new DebugId(_topLevelMethodOrdinal, CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
- }
-
- private DebugId GetClosureId(SyntaxNode syntax, ArrayBuilder closureDebugInfo)
- {
- Debug.Assert(syntax != null);
-
- DebugId closureId;
- DebugId previousClosureId;
- if (slotAllocatorOpt != null && slotAllocatorOpt.TryGetPreviousClosure(syntax, out previousClosureId))
- {
- closureId = previousClosureId;
- }
- else
- {
- closureId = new DebugId(closureDebugInfo.Count, CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal);
- }
-
- int syntaxOffset = _topLevelMethod.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree);
- closureDebugInfo.Add(new ClosureDebugInfo(syntaxOffset, closureId));
-
- return closureId;
- }
-
private DebugId GetLambdaId(SyntaxNode syntax, ClosureKind closureKind, int closureOrdinal)
{
Debug.Assert(syntax != null);
@@ -1298,88 +1364,44 @@ private DebugId GetLambdaId(SyntaxNode syntax, ClosureKind closureKind, int clos
return lambdaId;
}
- private SynthesizedLambdaMethod RewriteLambdaOrLocalFunction(
+ private SynthesizedClosureMethod RewriteLambdaOrLocalFunction(
IBoundLambdaOrFunction node,
out ClosureKind closureKind,
out NamedTypeSymbol translatedLambdaContainer,
- out LambdaFrame containerAsFrame,
+ out SynthesizedClosureEnvironment containerAsFrame,
out BoundNode lambdaScope,
out DebugId topLevelMethodId,
out DebugId lambdaId)
{
- ImmutableArray structClosures;
- int closureOrdinal;
- if (_analysis.LambdaScopes.TryGetValue(node.Symbol, out lambdaScope))
- {
- containerAsFrame = _frames[lambdaScope];
- structClosures = _analysis.CanTakeRefParameters(node.Symbol)
- ? GetStructClosures(containerAsFrame, lambdaScope)
- : default(ImmutableArray);
+ Analysis.Closure closure = Analysis.GetClosureInTree(_analysis.ScopeTree, node.Symbol);
+ var synthesizedMethod = closure.SynthesizedLoweredMethod;
+ Debug.Assert(synthesizedMethod != null);
- if (containerAsFrame?.IsValueType == true)
- {
- // Lower directly onto the containing type
- containerAsFrame = null;
- lambdaScope = null;
- closureKind = ClosureKind.Static; // not exactly... but we've rewritten the receiver to be a by-ref parameter
- translatedLambdaContainer = _topLevelMethod.ContainingType;
- closureOrdinal = LambdaDebugInfo.StaticClosureOrdinal;
- }
- else
- {
- closureKind = ClosureKind.General;
- translatedLambdaContainer = containerAsFrame;
- closureOrdinal = containerAsFrame.ClosureOrdinal;
- }
- }
- else if (Analysis.GetClosureInTree(_analysis.ScopeTree, node.Symbol).CapturedVariables.Count == 0)
+ closureKind = synthesizedMethod.ClosureKind;
+ translatedLambdaContainer = synthesizedMethod.ContainingType;
+ containerAsFrame = translatedLambdaContainer as SynthesizedClosureEnvironment;
+ topLevelMethodId = _analysis.GetTopLevelMethodId();
+ lambdaId = synthesizedMethod.LambdaId;
+
+ if (closure.ContainingEnvironmentOpt != null)
{
- if (_analysis.MethodsConvertedToDelegates.Contains(node.Symbol))
- {
- translatedLambdaContainer = containerAsFrame = GetStaticFrame(Diagnostics, node);
- closureKind = ClosureKind.Singleton;
- closureOrdinal = LambdaDebugInfo.StaticClosureOrdinal;
- }
- else
+ // Find the scope of the containing environment
+ BoundNode tmpScope = null;
+ Analysis.VisitScopeTree(_analysis.ScopeTree, scope =>
{
- containerAsFrame = null;
- translatedLambdaContainer = _topLevelMethod.ContainingType;
- closureKind = ClosureKind.Static;
- closureOrdinal = LambdaDebugInfo.StaticClosureOrdinal;
- }
- structClosures = default;
+ if (scope.DeclaredEnvironments.Contains(closure.ContainingEnvironmentOpt))
+ {
+ tmpScope = scope.BoundNode;
+ }
+ });
+ Debug.Assert(tmpScope != null);
+ lambdaScope = tmpScope;
}
else
{
- // GetStructClosures is currently overly aggressive in gathering
- // closures since the only information it has at this point is
- // NeedsParentFrame, which doesn't say what exactly is needed from
- // the parent frame. If `this` is captured, that's enough to mark
- // NeedsParentFrame for the current closure, so we need to gather
- // struct closures for all intermediate frames, even if they only
- // strictly need `this`.
- if (_analysis.CanTakeRefParameters(node.Symbol))
- {
- lambdaScope = _analysis.ScopeParent[node.Body];
- _ = _frames.TryGetValue(lambdaScope, out containerAsFrame);
- structClosures = GetStructClosures(containerAsFrame, lambdaScope);
- }
- else
- {
- structClosures = default;
- }
-
- containerAsFrame = null;
- translatedLambdaContainer = _topLevelMethod.ContainingType;
- closureKind = ClosureKind.ThisOnly;
- closureOrdinal = LambdaDebugInfo.ThisOnlyClosureOrdinal;
+ lambdaScope = null;
}
-
- // Move the body of the lambda to a freshly generated synthetic method on its frame.
- topLevelMethodId = GetTopLevelMethodId();
- lambdaId = GetLambdaId(node.Syntax, closureKind, closureOrdinal);
-
- var synthesizedMethod = new SynthesizedLambdaMethod(translatedLambdaContainer, structClosures, closureKind, _topLevelMethod, topLevelMethodId, node, lambdaId);
+
CompilationState.ModuleBuilderOpt.AddSynthesizedDefinition(translatedLambdaContainer, synthesizedMethod);
foreach (var parameter in node.Symbol.Parameters)
@@ -1387,11 +1409,6 @@ private SynthesizedLambdaMethod RewriteLambdaOrLocalFunction(
_parameterMap.Add(parameter, synthesizedMethod.Parameters[parameter.Ordinal]);
}
- if (node is BoundLocalFunctionStatement)
- {
- _localFunctionMap[((BoundLocalFunctionStatement)node).Symbol] = new MappedLocalFunction(synthesizedMethod, closureKind);
- }
-
// rewrite the lambda body as the generated method's body
var oldMethod = _currentMethod;
var oldFrameThis = _currentFrameThis;
@@ -1414,7 +1431,6 @@ private SynthesizedLambdaMethod RewriteLambdaOrLocalFunction(
else
{
_currentFrameThis = synthesizedMethod.ThisParameter;
- _innermostFramePointer = null;
_framePointers.TryGetValue(translatedLambdaContainer, out _innermostFramePointer);
}
@@ -1438,47 +1454,6 @@ private SynthesizedLambdaMethod RewriteLambdaOrLocalFunction(
return synthesizedMethod;
}
- ///
- /// Builds a list of all the struct-based closure environments that will
- /// need to be passed as arguments to the method.
- ///
- private ImmutableArray GetStructClosures(LambdaFrame containerAsFrame, BoundNode lambdaScope)
- {
- var structClosureParamBuilder = ArrayBuilder.GetInstance();
- while (containerAsFrame?.IsValueType == true ||
- _analysis.NeedsParentFrame.Contains(lambdaScope))
- {
- if (containerAsFrame?.IsValueType == true)
- {
- structClosureParamBuilder.Add(containerAsFrame);
- }
- if (_analysis.NeedsParentFrame.Contains(lambdaScope)
- && FindParentFrame(ref containerAsFrame, ref lambdaScope))
- {
- continue;
- }
-
- // can happen when scope no longer needs parent frame, or we're at the outermost level and the "parent frame" is top level "this".
- break;
- }
-
- // Reverse it because we're going from inner to outer, and parameters are in order of outer to inner
- structClosureParamBuilder.ReverseContents();
- return structClosureParamBuilder.ToImmutableAndFree();
-
- bool FindParentFrame(ref LambdaFrame container, ref BoundNode scope)
- {
- while (_analysis.ScopeParent.TryGetValue(scope, out scope))
- {
- if (_frames.TryGetValue(scope, out container))
- {
- return true;
- }
- }
- return false;
- }
- }
-
private void AddSynthesizedMethod(MethodSymbol method, BoundStatement body)
{
if (_synthesizedMethods == null)
@@ -1510,11 +1485,11 @@ private BoundNode RewriteLambdaConversion(BoundLambda node)
ClosureKind closureKind;
NamedTypeSymbol translatedLambdaContainer;
- LambdaFrame containerAsFrame;
+ SynthesizedClosureEnvironment containerAsFrame;
BoundNode lambdaScope;
DebugId topLevelMethodId;
DebugId lambdaId;
- SynthesizedLambdaMethod synthesizedMethod = RewriteLambdaOrLocalFunction(
+ SynthesizedClosureMethod synthesizedMethod = RewriteLambdaOrLocalFunction(
node,
out closureKind,
out translatedLambdaContainer,
@@ -1553,7 +1528,7 @@ private BoundNode RewriteLambdaConversion(BoundLambda node)
// NOTE: We require "lambdaScope != null".
// We do not want to introduce a field into an actual user's class (not a synthetic frame).
var shouldCacheInLoop = lambdaScope != null &&
- lambdaScope != _analysis.ScopeParent[node.Body] &&
+ lambdaScope != Analysis.GetScopeParent(_analysis.ScopeTree, node.Body).BoundNode &&
InLoopOrLambda(node.Syntax, lambdaScope.Syntax);
if (shouldCacheForStaticMethod || shouldCacheInLoop)
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/PartiallyLoweredLocalFunctionReference.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/PartiallyLoweredLocalFunctionReference.cs
deleted file mode 100644
index d7f39860d33eb..0000000000000
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/PartiallyLoweredLocalFunctionReference.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Microsoft.CodeAnalysis.Semantics;
-
-namespace Microsoft.CodeAnalysis.CSharp
-{
- ///
- /// This represents a partially lowered local function reference (e.g.,
- /// a local function call or delegate conversion) with relevant proxies
- /// attached. It will later be rewritten by the
- /// into a
- /// proper call.
- ///
- internal class PartiallyLoweredLocalFunctionReference : BoundExpression
- {
- private const BoundKind s_privateKind = (BoundKind)byte.MaxValue;
-
- public BoundExpression UnderlyingNode { get; }
- public Dictionary Proxies { get; }
-
- public PartiallyLoweredLocalFunctionReference(
- BoundExpression underlying,
- Dictionary proxies)
- : base(s_privateKind, underlying.Syntax, underlying.Type)
- {
- UnderlyingNode = underlying;
- Proxies = proxies;
- }
-
- public override BoundNode Accept(BoundTreeVisitor visitor) =>
- visitor.Visit(this);
-
- protected override OperationKind ExpressionKind
- {
- get
- {
- throw new InvalidOperationException();
- }
- }
-
- public override void Accept(OperationVisitor visitor)
- {
- throw new InvalidOperationException();
- }
-
- public override TResult Accept(OperationVisitor visitor, TArgument argument)
- {
- throw new InvalidOperationException();
- }
- }
-}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaFrame.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureEnvironment.cs
similarity index 56%
rename from src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaFrame.cs
rename to src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureEnvironment.cs
index 43c5bd3a8729e..67b4377b6d42d 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaFrame.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureEnvironment.cs
@@ -1,44 +1,54 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
///
- /// A class that represents the set of variables in a scope that have been
- /// captured by lambdas within that scope.
+ /// The synthesized type added to a compilation to hold captured variables for closures.
///
- internal sealed class LambdaFrame : SynthesizedContainer, ISynthesizedMethodBodyImplementationSymbol
+ internal sealed class SynthesizedClosureEnvironment : SynthesizedContainer, ISynthesizedMethodBodyImplementationSymbol
{
- private readonly TypeKind _typeKind;
private readonly MethodSymbol _topLevelMethod;
- private readonly MethodSymbol _containingMethod;
- private readonly MethodSymbol _constructor;
- private readonly MethodSymbol _staticConstructor;
- private readonly FieldSymbol _singletonCache;
internal readonly SyntaxNode ScopeSyntaxOpt;
internal readonly int ClosureOrdinal;
-
- internal LambdaFrame(MethodSymbol topLevelMethod, MethodSymbol containingMethod, bool isStruct, SyntaxNode scopeSyntaxOpt, DebugId methodId, DebugId closureId)
+ ///
+ /// The closest method/lambda that this frame is originally from. Null if nongeneric static closure.
+ /// Useful because this frame's type parameters are constructed from this method and all methods containing this method.
+ ///
+ internal readonly MethodSymbol OriginalContainingMethodOpt;
+ internal readonly FieldSymbol SingletonCache;
+ internal readonly MethodSymbol StaticConstructor;
+
+ public override TypeKind TypeKind { get; }
+ internal override MethodSymbol Constructor { get; }
+
+ internal SynthesizedClosureEnvironment(
+ MethodSymbol topLevelMethod,
+ MethodSymbol containingMethod,
+ bool isStruct,
+ SyntaxNode scopeSyntaxOpt,
+ DebugId methodId,
+ DebugId closureId)
: base(MakeName(scopeSyntaxOpt, methodId, closureId), containingMethod)
{
- _typeKind = isStruct ? TypeKind.Struct : TypeKind.Class;
+ TypeKind = isStruct ? TypeKind.Struct : TypeKind.Class;
_topLevelMethod = topLevelMethod;
- _containingMethod = containingMethod;
- _constructor = isStruct ? null : new LambdaFrameConstructor(this);
+ OriginalContainingMethodOpt = containingMethod;
+ Constructor = isStruct ? null : new SynthesizedClosureEnvironmentConstructor(this);
this.ClosureOrdinal = closureId.Ordinal;
// static lambdas technically have the class scope so the scope syntax is null
if (scopeSyntaxOpt == null)
{
- _staticConstructor = new SynthesizedStaticConstructor(this);
+ StaticConstructor = new SynthesizedStaticConstructor(this);
var cacheVariableName = GeneratedNames.MakeCachedFrameInstanceFieldName();
- _singletonCache = new SynthesizedLambdaCacheFieldSymbol(this, this, cacheVariableName, topLevelMethod, isReadOnly: true, isStatic: true);
+ SingletonCache = new SynthesizedLambdaCacheFieldSymbol(this, this, cacheVariableName, topLevelMethod, isReadOnly: true, isStatic: true);
}
AssertIsClosureScopeSyntax(scopeSyntaxOpt);
@@ -77,69 +87,25 @@ private static void AssertIsClosureScopeSyntax(SyntaxNode syntaxOpt)
throw ExceptionUtilities.UnexpectedValue(syntaxOpt.Kind());
}
- public override TypeKind TypeKind
- {
- get { return _typeKind; }
- }
-
- internal override MethodSymbol Constructor
- {
- get { return _constructor; }
- }
-
- internal MethodSymbol StaticConstructor
- {
- get { return _staticConstructor; }
- }
-
- ///
- /// The closest method/lambda that this frame is originally from. Null if nongeneric static closure.
- /// Useful because this frame's type parameters are constructed from this method and all methods containing this method.
- ///
- internal MethodSymbol ContainingMethod
- {
- get { return _containingMethod; }
- }
-
public override ImmutableArray GetMembers()
{
var members = base.GetMembers();
- if ((object)_staticConstructor != null)
+ if ((object)StaticConstructor != null)
{
- members = ImmutableArray.Create(_staticConstructor, _singletonCache).AddRange(members);
+ members = ImmutableArray.Create(StaticConstructor, SingletonCache).AddRange(members);
}
return members;
}
- internal FieldSymbol SingletonCache
- {
- get { return _singletonCache; }
- }
-
// display classes for static lambdas do not have any data and can be serialized.
- internal override bool IsSerializable
- {
- get { return (object)_singletonCache != null; }
- }
+ internal override bool IsSerializable => (object)SingletonCache != null;
- public override Symbol ContainingSymbol
- {
- get { return _topLevelMethod.ContainingSymbol; }
- }
+ public override Symbol ContainingSymbol => _topLevelMethod.ContainingSymbol;
- bool ISynthesizedMethodBodyImplementationSymbol.HasMethodBodyDependency
- {
- get
- {
- // the lambda method contains user code from the lambda:
- return true;
- }
- }
+ // The lambda method contains user code from the lambda
+ bool ISynthesizedMethodBodyImplementationSymbol.HasMethodBodyDependency => true;
- IMethodSymbol ISynthesizedMethodBodyImplementationSymbol.Method
- {
- get { return _topLevelMethod; }
- }
+ IMethodSymbol ISynthesizedMethodBodyImplementationSymbol.Method => _topLevelMethod;
}
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaFrameConstructor.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureEnvironmentConstructor.cs
similarity index 72%
rename from src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaFrameConstructor.cs
rename to src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureEnvironmentConstructor.cs
index b5e90ce0f5a28..76dd6c2642099 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaFrameConstructor.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureEnvironmentConstructor.cs
@@ -4,9 +4,9 @@
namespace Microsoft.CodeAnalysis.CSharp
{
- internal sealed class LambdaFrameConstructor : SynthesizedInstanceConstructor, ISynthesizedMethodBodyImplementationSymbol
+ internal sealed class SynthesizedClosureEnvironmentConstructor : SynthesizedInstanceConstructor, ISynthesizedMethodBodyImplementationSymbol
{
- internal LambdaFrameConstructor(LambdaFrame frame)
+ internal SynthesizedClosureEnvironmentConstructor(SynthesizedClosureEnvironment frame)
: base(frame)
{
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedLambdaMethod.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureMethod.cs
similarity index 65%
rename from src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedLambdaMethod.cs
rename to src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureMethod.cs
index f8cd67bdc9ccc..6d27d06955a7c 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedLambdaMethod.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/SynthesizedClosureMethod.cs
@@ -12,82 +12,112 @@ namespace Microsoft.CodeAnalysis.CSharp
///
/// A method that results from the translation of a single lambda expression.
///
- internal sealed class SynthesizedLambdaMethod : SynthesizedMethodBaseSymbol, ISynthesizedMethodBodyImplementationSymbol
+ internal sealed class SynthesizedClosureMethod : SynthesizedMethodBaseSymbol, ISynthesizedMethodBodyImplementationSymbol
{
private readonly MethodSymbol _topLevelMethod;
- private readonly ImmutableArray _structClosures;
+ private readonly ImmutableArray _structEnvironments;
- internal SynthesizedLambdaMethod(
+ internal readonly DebugId LambdaId;
+
+ internal SynthesizedClosureMethod(
NamedTypeSymbol containingType,
- ImmutableArray structClosures,
+ ImmutableArray structEnvironments,
ClosureKind closureKind,
MethodSymbol topLevelMethod,
DebugId topLevelMethodId,
- IBoundLambdaOrFunction lambdaNode,
+ MethodSymbol originalMethod,
+ SyntaxReference blockSyntax,
DebugId lambdaId)
: base(containingType,
- lambdaNode.Symbol,
+ originalMethod,
null,
- lambdaNode.Syntax.SyntaxTree.GetReference(lambdaNode.Body.Syntax),
- lambdaNode.Syntax.GetLocation(),
- lambdaNode is BoundLocalFunctionStatement ?
- MakeName(topLevelMethod.Name, lambdaNode.Symbol.Name, topLevelMethodId, closureKind, lambdaId) :
- MakeName(topLevelMethod.Name, topLevelMethodId, closureKind, lambdaId),
- (closureKind == ClosureKind.ThisOnly ? DeclarationModifiers.Private : DeclarationModifiers.Internal)
- | (closureKind == ClosureKind.Static ? DeclarationModifiers.Static : 0)
- | (lambdaNode.Symbol.IsAsync ? DeclarationModifiers.Async : 0))
+ blockSyntax,
+ originalMethod.DeclaringSyntaxReferences[0].GetLocation(),
+ originalMethod is LocalFunctionSymbol
+ ? MakeName(topLevelMethod.Name, originalMethod.Name, topLevelMethodId, closureKind, lambdaId)
+ : MakeName(topLevelMethod.Name, topLevelMethodId, closureKind, lambdaId),
+ MakeDeclarationModifiers(closureKind, originalMethod))
{
_topLevelMethod = topLevelMethod;
ClosureKind = closureKind;
+ LambdaId = lambdaId;
TypeMap typeMap;
ImmutableArray typeParameters;
ImmutableArray constructedFromTypeParameters;
- LambdaFrame lambdaFrame;
- lambdaFrame = this.ContainingType as LambdaFrame;
+ var lambdaFrame = ContainingType as SynthesizedClosureEnvironment;
switch (closureKind)
{
case ClosureKind.Singleton: // all type parameters on method (except the top level method's)
case ClosureKind.General: // only lambda's type parameters on method (rest on class)
Debug.Assert(lambdaFrame != null);
- typeMap = lambdaFrame.TypeMap.WithConcatAlphaRename(lambdaNode.Symbol, this, out typeParameters, out constructedFromTypeParameters, lambdaFrame.ContainingMethod);
+ typeMap = lambdaFrame.TypeMap.WithConcatAlphaRename(
+ originalMethod,
+ this,
+ out typeParameters,
+ out constructedFromTypeParameters,
+ lambdaFrame.OriginalContainingMethodOpt);
break;
case ClosureKind.ThisOnly: // all type parameters on method
case ClosureKind.Static:
Debug.Assert(lambdaFrame == null);
- typeMap = TypeMap.Empty.WithConcatAlphaRename(lambdaNode.Symbol, this, out typeParameters, out constructedFromTypeParameters, null);
+ typeMap = TypeMap.Empty.WithConcatAlphaRename(
+ originalMethod,
+ this,
+ out typeParameters,
+ out constructedFromTypeParameters,
+ stopAt: null);
break;
default:
throw ExceptionUtilities.UnexpectedValue(closureKind);
}
- if (!structClosures.IsDefaultOrEmpty && typeParameters.Length != 0)
+ if (!structEnvironments.IsDefaultOrEmpty && typeParameters.Length != 0)
{
- var constructedStructClosures = ArrayBuilder.GetInstance();
- foreach (var closure in structClosures)
+ var constructedStructClosures = ArrayBuilder.GetInstance();
+ foreach (var env in structEnvironments)
{
- var frame = (LambdaFrame)closure;
NamedTypeSymbol constructed;
- if (frame.Arity == 0)
+ if (env.Arity == 0)
{
- constructed = frame;
+ constructed = env;
}
else
{
- var originals = frame.ConstructedFromTypeParameters;
+ var originals = env.ConstructedFromTypeParameters;
var newArgs = typeMap.SubstituteTypeParameters(originals);
- constructed = frame.Construct(newArgs);
+ constructed = env.Construct(newArgs);
}
constructedStructClosures.Add(constructed);
}
- structClosures = constructedStructClosures.ToImmutableAndFree();
+ _structEnvironments = constructedStructClosures.ToImmutableAndFree();
+ }
+ else
+ {
+ _structEnvironments = ImmutableArray.CastUp(structEnvironments);
}
- _structClosures = structClosures;
AssignTypeMapAndTypeParameters(typeMap, typeParameters);
}
+ private static DeclarationModifiers MakeDeclarationModifiers(ClosureKind closureKind, MethodSymbol originalMethod)
+ {
+ var mods = closureKind == ClosureKind.ThisOnly ? DeclarationModifiers.Private : DeclarationModifiers.Internal;
+
+ if (closureKind == ClosureKind.Static)
+ {
+ mods |= DeclarationModifiers.Static;
+ }
+
+ if (originalMethod.IsAsync)
+ {
+ mods |= DeclarationModifiers.Async;
+ }
+
+ return mods;
+ }
+
private static string MakeName(string topLevelMethodName, string localFunctionName, DebugId topLevelMethodId, ClosureKind closureKind, DebugId lambdaId)
{
return GeneratedNames.MakeLocalFunctionName(
@@ -125,8 +155,9 @@ private static string MakeName(string topLevelMethodName, DebugId topLevelMethod
// UNDONE: names from the delegate. Does it really matter?
protected override ImmutableArray BaseMethodParameters => this.BaseMethod.Parameters;
- protected override ImmutableArray ExtraSynthesizedRefParameters => _structClosures;
- internal int ExtraSynthesizedParameterCount => this._structClosures.IsDefault ? 0 : this._structClosures.Length;
+ protected override ImmutableArray ExtraSynthesizedRefParameters
+ => ImmutableArray.CastUp(_structEnvironments);
+ internal int ExtraSynthesizedParameterCount => this._structEnvironments.IsDefault ? 0 : this._structEnvironments.Length;
internal override bool GenerateDebugInfo => !this.IsAsync;
internal override bool IsExpressionBodied => false;
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
index a9888613e2a95..1cdf90c93b4c8 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs
@@ -1822,24 +1822,6 @@ private BoundExpression RewriteNullableNullEquality(
return result;
}
- private static MethodSymbol GetTruthOperator(TypeSymbol type, bool negative)
- {
- string name = negative ? WellKnownMemberNames.FalseOperatorName : WellKnownMemberNames.TrueOperatorName;
- var operators = ((NamedTypeSymbol)type.StrippedType()).GetOperators(name);
- Debug.Assert(!operators.IsEmpty);
- for (int i = 0; i < operators.Length; ++i)
- {
- Debug.Assert(operators[i].ParameterCount == 1);
- if (operators[i].ParameterTypes[0] == type)
- {
- return operators[i];
- }
- }
-
- Debug.Assert(false, "How did we bind a user-defined logical operator or dynamic logical Boolean operator without operator false or operator true?");
- return null;
- }
-
private BoundExpression RewriteStringEquality(BoundBinaryOperator oldNode, SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, SpecialMember member)
{
if (oldNode != null && (loweredLeft.ConstantValue == ConstantValue.Null || loweredRight.ConstantValue == ConstantValue.Null))
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
index 9a9d324ef5606..20ca8231878ad 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
@@ -439,7 +439,7 @@ private ImmutableArray MakeArguments(
//
// By contrast:
//
- // Foo(z: this.p, y: this.Q(), x: (object)10)
+ // Goo(z: this.p, y: this.Q(), x: (object)10)
//
// The boxing of 10 can be reordered, but the fetch of this.p has to happen before the
// call to this.Q() because the call could change the value of this.p.
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs
index 316f4ab4bd20f..55f8a9f776ed0 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs
@@ -94,7 +94,7 @@ private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentO
if (isPossibleEventHandlerOperation)
{
- // IsEvent("Foo", dyn) ? InvokeMember("{add|remove}_Foo", dyn, RHS) : rewrittenAssignment
+ // IsEvent("Goo", dyn) ? InvokeMember("{add|remove}_Goo", dyn, RHS) : rewrittenAssignment
var memberAccess = (BoundDynamicMemberAccess)transformedLHS;
var isEventCondition = _dynamicFactory.MakeDynamicIsEventTest(memberAccess.Name, memberAccess.Receiver);
@@ -648,9 +648,9 @@ private BoundExpression SpillArrayElementAccess(
/// are not captured.
///
/// Example:
- /// l += foo(ref l);
+ /// l += goo(ref l);
///
- /// even though l is a local, we must access it via a temp since "foo(ref l)" may change it
+ /// even though l is a local, we must access it via a temp since "goo(ref l)" may change it
/// on between accesses.
///
internal static bool CanChangeValueBetweenReads(
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs
index 075e417394dde..89e9ed6096d24 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs
@@ -650,16 +650,8 @@ private BoundExpression RewriteTupleConversion(
for (int i = 0; i < numElements; i++)
{
- var field = srcElementFields[i];
-
- DiagnosticInfo useSiteInfo = field.GetUseSiteDiagnostic();
- if ((object)useSiteInfo != null && useSiteInfo.Severity == DiagnosticSeverity.Error)
- {
- Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location);
- }
- var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty);
+ var fieldAccess = MakeTupleFieldAccessAndReportUseSiteDiagnostics(savedTuple, syntax, srcElementFields[i]);
var convertedFieldAccess = MakeConversionNode(syntax, fieldAccess, elementConversions[i], destElementTypes[i], @checked, explicitCastInCode);
-
fieldAccessorsBuilder.Add(convertedFieldAccess);
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs
index 2265088d8a49e..a8ab8094cce5b 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DeconstructionAssignmentOperator.cs
@@ -221,18 +221,7 @@ private ImmutableArray AccessTupleFields(BoundExpression expres
var builder = ArrayBuilder.GetInstance(numElements);
for (int i = 0; i < numElements; i++)
{
- var field = fields[i];
-
- // Use default field rather than implicitly named fields since
- // fields from inferred names are not usable in C# 7.0.
- field = field.CorrespondingTupleField ?? field;
-
- DiagnosticInfo useSiteInfo = field.GetUseSiteDiagnostic();
- if ((object)useSiteInfo != null && useSiteInfo.Severity == DiagnosticSeverity.Error)
- {
- Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, expression.Syntax.Location);
- }
- var fieldAccess = MakeTupleFieldAccess(expression.Syntax, field, tuple, null, LookupResultKind.Empty);
+ var fieldAccess = MakeTupleFieldAccessAndReportUseSiteDiagnostics(tuple, expression.Syntax, fields[i]);
builder.Add(fieldAccess);
}
return builder.ToImmutableAndFree();
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Field.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Field.cs
index 92942a3872be5..f178d2737224d 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Field.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Field.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Symbols;
-using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp
{
@@ -90,5 +89,20 @@ private BoundExpression MakeTupleFieldAccess(
// make a field access for the most local access
return _factory.Field(rewrittenReceiver, underlyingField);
}
+
+ private BoundExpression MakeTupleFieldAccessAndReportUseSiteDiagnostics(BoundExpression tuple, SyntaxNode syntax, FieldSymbol field)
+ {
+ // Use default field rather than implicitly named fields since
+ // fields from inferred names are not usable in C# 7.0.
+ field = field.CorrespondingTupleField ?? field;
+
+ DiagnosticInfo useSiteInfo = field.GetUseSiteDiagnostic();
+ if ((object)useSiteInfo != null && useSiteInfo.Severity == DiagnosticSeverity.Error)
+ {
+ Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location);
+ }
+
+ return MakeTupleFieldAccess(syntax, field, tuple, null, LookupResultKind.Empty);
+ }
}
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs
index 39b2ce911f909..1c3becde5b662 100644
--- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs
@@ -269,28 +269,22 @@ public BoundFieldAccess Field(BoundExpression receiver, FieldSymbol f)
public BoundExpression Property(WellKnownMember member)
{
- var propertySym = WellKnownMember(member) as PropertySymbol;
- //if (propertySym == null) return BoundBadExpression
- Binder.ReportUseSiteDiagnostics(propertySym, Diagnostics, Syntax);
- Debug.Assert(propertySym.IsStatic);
- return Call(null, propertySym.GetMethod);
+ return Property(null, member);
}
- public BoundExpression Property(BoundExpression receiver, WellKnownMember member)
+ public BoundExpression Property(BoundExpression receiverOpt, WellKnownMember member)
{
- var propertySym = WellKnownMember(member) as PropertySymbol;
- Debug.Assert(!propertySym.IsStatic);
- Debug.Assert(receiver.Type.GetMembers(propertySym.Name).OfType().Single() == propertySym);
- //if (propertySym == null) return BoundBadExpression
+ var propertySym = (PropertySymbol)WellKnownMember(member);
+ Debug.Assert(receiverOpt == null ||
+ receiverOpt.Type.GetMembers(propertySym.Name).OfType().Single() == propertySym);
Binder.ReportUseSiteDiagnostics(propertySym, Diagnostics, Syntax);
- Debug.Assert(!propertySym.IsStatic);
- return Call(receiver, propertySym.GetMethod);
+ return Property(receiverOpt, propertySym);
}
- public BoundExpression Property(BoundExpression receiver, PropertySymbol property)
+ public BoundExpression Property(BoundExpression receiverOpt, PropertySymbol property)
{
- Debug.Assert(!property.IsStatic);
- return Call(receiver, property.GetMethod); // TODO: should we use property.GetBaseProperty().GetMethod to ensure we generate a call to the overridden method?
+ Debug.Assert((receiverOpt == null) == property.IsStatic);
+ return Call(receiverOpt, property.GetMethod); // TODO: should we use property.GetBaseProperty().GetMethod to ensure we generate a call to the overridden method?
}
public NamedTypeSymbol SpecialType(SpecialType st)
@@ -601,7 +595,7 @@ public BoundExpression StaticCall(TypeSymbol receiver, MethodSymbol method, para
{
if ((object)method == null)
{
- return new BoundBadExpression(Syntax, default(LookupResultKind), ImmutableArray.Empty, args.AsImmutableOrNull(), receiver);
+ return new BoundBadExpression(Syntax, default(LookupResultKind), ImmutableArray.Empty, args.AsImmutable(), receiver);
}
return Call(null, method, args);
diff --git a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs
index 30f140822c483..dc4c5b63d46c1 100644
--- a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs
+++ b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs
@@ -258,13 +258,13 @@ private DirectiveTriviaSyntax ParseDefineOrUndefDirective(SyntaxToken hash, Synt
/// Resulting span: [first non-whitespace char, EOD)
///
/// Examples (pipes indicate span):
- /// #error |foo|
- /// #error |foo|
- /// #error |foo |
- /// #error |foo baz|
- /// #error |//foo|
- /// #error |/*foo*/|
- /// #error |/*foo|
+ /// #error |goo|
+ /// #error |goo|
+ /// #error |goo |
+ /// #error |goo baz|
+ /// #error |//goo|
+ /// #error |/*goo*/|
+ /// #error |/*goo|
///
/// The '#' token.
/// The 'error' or 'warning' token.
diff --git a/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs b/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs
index b91fd6409ac22..0aab7bc549fe9 100644
--- a/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs
+++ b/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs
@@ -68,7 +68,7 @@ public DocumentationCommentTriviaSyntax ParseDocumentationComment(out bool isTer
// It's possible that we finish parsing the xml, and we are still left in the middle
// of an Xml comment. For example,
//
- // ///
+ // ///
// ^
// In this case, we stop at the caret. We need to ensure that we consume the remainder
// of the doc comment here, since otherwise we will return the lexer to the state
diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
index 958b801b818f3..580820a9226d6 100644
--- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
+++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
@@ -751,9 +751,9 @@ private bool IsNamespaceMemberStartOrStop()
private bool ScanExternAliasDirective()
{
// The check also includes the ending semicolon so that we can disambiguate among:
- // extern alias foo;
- // extern alias foo();
- // extern alias foo { get; }
+ // extern alias goo;
+ // extern alias goo();
+ // extern alias goo { get; }
return this.CurrentToken.Kind == SyntaxKind.ExternKeyword
&& this.PeekToken(1).Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).ContextualKind == SyntaxKind.AliasKeyword
@@ -818,7 +818,7 @@ private UsingDirectiveSyntax ParseUsingDirective()
//new directive. e.g.
//
// using
- // namespace Foo {
+ // namespace Goo {
// //...
// }
//
@@ -1276,7 +1276,7 @@ private bool ShouldAsyncBeTreatedAsModifier(bool parsingStatementNotDeclaration)
{
// We've seen "async TypeName". Now we have to determine if we should we treat
// 'async' as a modifier. Or is the user actually writing something like
- // "public async Foo" where 'async' is actually the return type.
+ // "public async Goo" where 'async' is actually the return type.
if (IsPossibleMemberName())
{
@@ -2449,13 +2449,13 @@ private bool IsFieldDeclaration(bool isEvent)
var kind = this.PeekToken(1).Kind;
switch (kind)
{
- case SyntaxKind.DotToken: // Foo. explicit
- case SyntaxKind.ColonColonToken: // Foo:: explicit
- case SyntaxKind.LessThanToken: // Foo< explicit or generic method
- case SyntaxKind.OpenBraceToken: // Foo { property
- case SyntaxKind.EqualsGreaterThanToken: // Foo => property
+ case SyntaxKind.DotToken: // Goo. explicit
+ case SyntaxKind.ColonColonToken: // Goo:: explicit
+ case SyntaxKind.LessThanToken: // Goo< explicit or generic method
+ case SyntaxKind.OpenBraceToken: // Goo { property
+ case SyntaxKind.EqualsGreaterThanToken: // Goo => property
return false;
- case SyntaxKind.OpenParenToken: // Foo( method
+ case SyntaxKind.OpenParenToken: // Goo( method
return isEvent;
default:
return true;
@@ -2659,7 +2659,7 @@ private bool IsEndOfTypeParameterList()
{
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
- // void Foo[][] etc.), or pointer types
+ // (int, string, etc.), or array types (Goo[], A[][] etc.), or pointer types
// of things that must be types (int*, void**, etc.).
isDefinitelyTypeArgumentList = DetermineIfDefinitelyTypeArgumentList(isDefinitelyTypeArgumentList);
result = ScanTypeFlags.GenericTypeOrMethod;
@@ -5708,8 +5708,8 @@ private NameSyntax ParseQualifiedNameRight(
}
// If the left hand side is not an identifier name then the user has done
- // something like Foo.Bar::Blah. We've already made an error node for the
- // ::, so just pretend that they typed Foo.Bar.Blah and continue on.
+ // something like Goo.Bar::Blah. We've already made an error node for the
+ // ::, so just pretend that they typed Goo.Bar.Blah and continue on.
var identifierLeft = left as IdentifierNameSyntax;
if (identifierLeft == null)
@@ -9971,10 +9971,10 @@ private bool ScanCast()
}
// If we have any of the following, we know it must be a cast:
- // 1) (Foo*)bar;
- // 2) (Foo?)bar;
+ // 1) (Goo*)bar;
+ // 2) (Goo?)bar;
// 3) "(int)bar" or "(int[])bar"
- // 4) (G::Foo)bar
+ // 4) (G::Goo)bar
if (type == ScanTypeFlags.PointerOrMultiplication ||
type == ScanTypeFlags.NullableType ||
type == ScanTypeFlags.MustBeType ||
diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs
index c412c94a55d7c..b58239e7efb69 100644
--- a/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs
+++ b/src/Compilers/CSharp/Portable/SymbolDisplay/ObjectDisplay.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Text;
@@ -175,13 +176,24 @@ private static bool TryReplaceChar(char c, out string replaceWith)
return true;
}
- switch (CharUnicodeInfo.GetUnicodeCategory(c))
+ if (NeedsEscaping(CharUnicodeInfo.GetUnicodeCategory(c)))
+ {
+ replaceWith = "\\u" + ((int)c).ToString("x4");
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool NeedsEscaping(UnicodeCategory category)
+ {
+ switch (category)
{
case UnicodeCategory.Control:
case UnicodeCategory.OtherNotAssigned:
case UnicodeCategory.ParagraphSeparator:
case UnicodeCategory.LineSeparator:
- replaceWith = "\\u" + ((int)c).ToString("x4");
+ case UnicodeCategory.Surrogate:
return true;
default:
return false;
@@ -223,10 +235,32 @@ public static string FormatLiteral(string value, ObjectDisplayOptions options)
builder.Append(quote);
}
- foreach (var c in value)
+ for (int i = 0; i < value.Length; i++)
{
- string replaceWith;
- if (escapeNonPrintable && TryReplaceChar(c, out replaceWith))
+ char c = value[i];
+ if (escapeNonPrintable && CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Surrogate)
+ {
+ var category = CharUnicodeInfo.GetUnicodeCategory(value, i);
+ if (category == UnicodeCategory.Surrogate)
+ {
+ // an unpaired surrogate
+ builder.Append("\\u" + ((int)c).ToString("x4"));
+ }
+ else if (NeedsEscaping(category))
+ {
+ // a surrogate pair that needs to be escaped
+ var unicode = char.ConvertToUtf32(value, i);
+ builder.Append("\\U" + unicode.ToString("x8"));
+ i++; // skip the already-encoded second surrogate of the pair
+ }
+ else
+ {
+ // copy a printable surrogate pair directly
+ builder.Append(c);
+ builder.Append(value[++i]);
+ }
+ }
+ else if (escapeNonPrintable && TryReplaceChar(c, out var replaceWith))
{
builder.Append(replaceWith);
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs
index b32b91d07d295..05cd6fd2b4d38 100644
--- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs
@@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
///
/// An ErrorSymbol is used when the compiler cannot determine a symbol object to return because
- /// of an error. For example, if a field is declared "Foo x;", and the type "Foo" cannot be
+ /// of an error. For example, if a field is declared "Goo x;", and the type "Goo" cannot be
/// found, an ErrorSymbol is returned when asking the field "x" what it's type is.
///
internal abstract partial class ErrorTypeSymbol : NamedTypeSymbol, IErrorTypeSymbol
diff --git a/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs
index d1cc49a042ef2..8cf08b6eb28ef 100644
--- a/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/ExtendedErrorTypeSymbol.cs
@@ -200,9 +200,9 @@ internal override ImmutableArray GetDeclaredInterfaces(ConsList
/// type slot.
///
/// Sometimes we will return the original definition of the intended symbol. For example, if we see
- /// ]]> and we have an IFoo with a different arity or accessibility
- /// (e.g. ]]> was constructed from an error symbol based on ]]>),
- /// then we'll return ]]>, rather than trying to construct a corresponding closed
+ /// ]]> and we have an IGoo with a different arity or accessibility
+ /// (e.g. ]]> was constructed from an error symbol based on ]]>),
+ /// then we'll return ]]>, rather than trying to construct a corresponding closed
/// type (which may not be difficult/possible in the case of nested types or mismatched arities).
///
/// NOTE: Any non-null type symbol returned is guaranteed not to be an error type.
diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs
index 0d2eb1cac430a..05669ea1d34ca 100644
--- a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs
@@ -71,7 +71,7 @@ internal override DiagnosticInfo ErrorInfo
if (containingAssembly.IsMissing)
{
- // error CS0012: The type 'Blah' is defined in an assembly that is not referenced. You must add a reference to assembly 'Foo'.
+ // error CS0012: The type 'Blah' is defined in an assembly that is not referenced. You must add a reference to assembly 'Goo'.
return new CSDiagnosticInfo(ErrorCode.ERR_NoTypeDef, this, containingAssembly.Identity);
}
else
diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs
index bee7216bad11e..fa2ca3a069ec0 100644
--- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs
@@ -1126,10 +1126,10 @@ internal override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticIn
// problems with constraints. We recurse into type *arguments* in the overload
// in ConstructedNamedTypeSymbol.
//
- // When we are binding a name with a nested type, Foo.Bar, then we ask for
- // use-site errors to be reported on both Foo and Foo.Bar. Therefore we should
+ // When we are binding a name with a nested type, Goo.Bar, then we ask for
+ // use-site errors to be reported on both Goo and Goo.Bar. Therefore we should
// not recurse into the containing type here; doing so will result in errors
- // being reported twice if Foo is bad.
+ // being reported twice if Goo is bad.
var @base = this.BaseTypeNoUseSiteDiagnostics;
if ((object)@base != null && @base.GetUnificationUseSiteDiagnosticRecursive(ref result, owner, ref checkedTypes))
diff --git a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs
index 84424a1809c0b..d662a6de8358e 100644
--- a/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/OverriddenOrHiddenMembersHelpers.cs
@@ -750,8 +750,8 @@ private static void FindOtherOverriddenMethodsInContainingType(Symbol representa
// NOTE: We're actually being more precise than Dev10 - we consider the fact that the runtime will also distinguish
// on the basis of return type. For example, consider the following signatures:
- // int Foo(ref int x)
- // long Foo(out int x)
+ // int Goo(ref int x)
+ // long Goo(out int x)
// Dev10 will warn that these methods are runtime ambiguous, even though they aren't really (because they are
// distinguished by their return types).
if (MemberSignatureComparer.RuntimeSignatureComparer.Equals(otherMember, representativeMember))
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs
index 45f90842a2693..aa78aa8cf24bc 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs
@@ -337,7 +337,7 @@ internal static Accessibility EffectiveAccessibility(DeclarationModifiers modifi
default:
// This happens when you have a mix of accessibilities.
//
- // i.e.: public private void Foo()
+ // i.e.: public private void Goo()
return Accessibility.Public;
}
}
@@ -358,7 +358,7 @@ internal static bool IsValidAccessibility(DeclarationModifiers modifiers)
default:
// This happens when you have a mix of accessibilities.
//
- // i.e.: public private void Foo()
+ // i.e.: public private void Goo()
return false;
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs
index 83128808abbbc..00c87922a4581 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs
@@ -472,6 +472,11 @@ internal sealed override TypeSymbol GetFieldType(ConsList fieldsBei
{
diagnosticsForFirstDeclarator.Add(ErrorCode.ERR_ImplicitlyTypedVariableMultipleDeclarator, typeSyntax.Location);
}
+ else if (this.IsConst && this.ContainingType.IsScriptClass)
+ {
+ // For const var in script, we won't try to bind the initializer (case below), as it can lead to an unbound recursion
+ type = null;
+ }
else
{
fieldsBeingBound = new ConsList(this, fieldsBeingBound);
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs
index e7526be75b2ad..cd7dfe53f0b49 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs
@@ -374,8 +374,7 @@ private Dictionary> MakeNameToMemb
if ((object)other != null)
{
- if (nts is SourceNamedTypeSymbol && other is SourceNamedTypeSymbol &&
- (nts as SourceNamedTypeSymbol).IsPartial && (other as SourceNamedTypeSymbol).IsPartial)
+ if ((nts as SourceNamedTypeSymbol)?.IsPartial == true && (other as SourceNamedTypeSymbol)?.IsPartial == true)
{
diagnostics.Add(ErrorCode.ERR_PartialTypeKindConflict, symbol.Locations[0], symbol);
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs
index 6c3b29447322d..2678734e71153 100644
--- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs
@@ -382,9 +382,9 @@ private int ComputeHashCode()
// may still be considered equal, we do not want to give different hashcode to such types.
//
// Example:
- // Having original method A.Foo() we create two _unconstructed_ methods
- // A.Foo
- // A.Foo
+ // Having original method A.Goo() we create two _unconstructed_ methods
+ // A.Goo
+ // A.Goo
// Note that V' and V" are type parameters substituted via alpha-renaming of original V
// These are different objects, but represent the same "type parameter at index 1"
//
diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs
index 52d6085552edd..34484afb49e18 100644
--- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs
@@ -29,8 +29,8 @@ internal abstract class SubstitutedNamedTypeSymbol : WrappedNamedTypeSymbol
// The container of a substituted named type symbol is typically a named type or a namespace.
// However, in some error-recovery scenarios it might be some other container. For example,
- // consider "int Foo = 123; Foo x = null;" What is the type of x? We construct an error
- // type symbol of arity one associated with local variable symbol Foo; when we construct
+ // consider "int Goo = 123; Goo x = null;" What is the type of x? We construct an error
+ // type symbol of arity one associated with local variable symbol Goo; when we construct
// that error type symbol with , the resulting substituted named type symbol has
// the same containing symbol as the local: it is contained in the method.
private readonly Symbol _newContainer;
diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs
index 01c11c45f2e90..d786cf8b86906 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs
@@ -384,6 +384,7 @@ internal static ImmutableArray GetDeclaringSyntaxReferenceHelpe
/// the "value" parameter for a property setter,
/// the parameters on indexer accessor methods (not on the indexer itself),
/// methods in anonymous types,
+ /// anonymous functions
///
public virtual bool IsImplicitlyDeclared
{
diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolDistinguisher.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolDistinguisher.cs
index bfb99f7869edd..dd493535639cb 100644
--- a/src/Compilers/CSharp/Portable/Symbols/SymbolDistinguisher.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/SymbolDistinguisher.cs
@@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp
{
///
/// Some error messages are particularly confusing if multiple placeholders are substituted
- /// with the same string. For example, "cannot convert from 'Foo' to 'Foo'". Usually, this
+ /// with the same string. For example, "cannot convert from 'Goo' to 'Goo'". Usually, this
/// occurs because there are two types in different contexts with the same qualified name.
/// The solution is to provide additional qualification on each symbol - either a source
/// location, an assembly path, or an assembly identity.
diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs
index 5508837fe7920..c7c536c7f5318 100644
--- a/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs
@@ -31,7 +31,7 @@ private static void GetAllPragmaWarningDirectives(SyntaxTree syntaxTree, ArrayBu
{
if (d.Kind() == SyntaxKind.PragmaWarningDirectiveTrivia)
{
- var w = d as PragmaWarningDirectiveTriviaSyntax;
+ var w = (PragmaWarningDirectiveTriviaSyntax)d;
// Ignore directives with errors (i.e., Unrecognized #pragma directive) and
// directives inside disabled code (by #if and #endif)
@@ -83,12 +83,12 @@ private static WarningStateMapEntry[] CreatePragmaWarningStateEntries(ImmutableA
var errorId = string.Empty;
if (currentErrorCode.Kind() == SyntaxKind.NumericLiteralExpression)
{
- var token = (currentErrorCode as LiteralExpressionSyntax).Token;
+ var token = ((LiteralExpressionSyntax)currentErrorCode).Token;
errorId = MessageProvider.Instance.GetIdForErrorCode((int)token.Value);
}
else if (currentErrorCode.Kind() == SyntaxKind.IdentifierName)
{
- errorId = (currentErrorCode as IdentifierNameSyntax).Identifier.ValueText;
+ errorId = ((IdentifierNameSyntax)currentErrorCode).Identifier.ValueText;
}
if (!string.IsNullOrWhiteSpace(errorId))
diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxEquivalence.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxEquivalence.cs
index 2d3bea0bf3959..cc5d96e20cb3e 100644
--- a/src/Compilers/CSharp/Portable/Syntax/SyntaxEquivalence.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxEquivalence.cs
@@ -135,7 +135,7 @@ private static bool AreEquivalentRecursive(GreenNode before, GreenNode after, Fu
// NOTE(cyrusn): Do we want to avoid going down into attribute expressions? I don't
// think we can avoid it as there are likely places in the compiler that use these
- // expressions. For example, if the user changes [InternalsVisibleTo("foo")] to
+ // expressions. For example, if the user changes [InternalsVisibleTo("goo")] to
// [InternalsVisibleTo("bar")] then that must count as a top level change as it
// affects symbol visibility. Perhaps we could enumerate the places in the compiler
// that use the values inside source attributes and we can check if we're in an
diff --git a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj
index 4a524ddb0b97c..1485927750c5a 100644
--- a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj
+++ b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj
@@ -35,6 +35,7 @@
+
@@ -59,4 +60,4 @@
-
\ No newline at end of file
+
diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
index 8c686fdc2fbcb..adafe9cc93ef6 100644
--- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
+++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
@@ -22,6 +22,8 @@
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
+using Microsoft.DiaSymReader;
+using Roslyn.Test.PdbUtilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
@@ -175,7 +177,7 @@ public void ResponseFiles_RelativePaths()
var dirX = baseDir.CreateDirectory("x");
var dirAB = baseDir.CreateDirectory("a b");
var dirSubDir = baseDir.CreateDirectory("subdir");
- var dirFoo = parentDir.CreateDirectory("foo");
+ var dirGoo = parentDir.CreateDirectory("goo");
var dirBar = parentDir.CreateDirectory("bar");
string basePath = baseDir.Path;
@@ -188,7 +190,7 @@ public void ResponseFiles_RelativePaths()
/r:..\v4.0.30319\System.dll
/r:.\System.Data.dll
a.cs @""..\c.rsp"" @\d.rsp
-/libpaths:..\foo;../bar;""a b""
+/libpaths:..\goo;../bar;""a b""
"
},
{ Path.Combine(dirSubDir.Path, @"b.rsp"), @"
@@ -216,7 +218,7 @@ a.cs @""..\c.rsp"" @\d.rsp
AssertEx.Equal(new[] { "first.cs", "second.cs", "b.cs", "a.cs", "c.cs", "d.cs", "last.cs" }.Select(prependBasePath), resolvedSourceFiles);
AssertEx.Equal(new[] { typeof(object).Assembly.Location, @"..\v4.0.30319\System.dll", @".\System.Data.dll" }, references);
- AssertEx.Equal(new[] { RuntimeEnvironment.GetRuntimeDirectory() }.Concat(new[] { @"x", @"..\foo", @"../bar", @"a b" }.Select(prependBasePath)), args.ReferencePaths.ToArray());
+ AssertEx.Equal(new[] { RuntimeEnvironment.GetRuntimeDirectory() }.Concat(new[] { @"x", @"..\goo", @"../bar", @"a b" }.Select(prependBasePath)), args.ReferencePaths.ToArray());
Assert.Equal(basePath, args.BaseDirectory);
}
@@ -398,7 +400,7 @@ public void Win32ResourceArguments()
args = new string[]
{
- @"/Win32Res:foo.win32data:bar.win32data2"
+ @"/Win32Res:goo.win32data:bar.win32data2"
};
parsedArgs = DefaultParse(args, _baseDirectory);
@@ -409,7 +411,7 @@ public void Win32ResourceArguments()
args = new string[]
{
- @"/Win32icon:foo.win32data:bar.win32data2"
+ @"/Win32icon:goo.win32data:bar.win32data2"
};
parsedArgs = DefaultParse(args, _baseDirectory);
@@ -420,7 +422,7 @@ public void Win32ResourceArguments()
args = new string[]
{
- @"/Win32manifest:foo.win32data:bar.win32data2"
+ @"/Win32manifest:goo.win32data:bar.win32data2"
};
parsedArgs = DefaultParse(args, _baseDirectory);
@@ -433,11 +435,11 @@ public void Win32ResourceArguments()
[Fact]
public void Win32ResConflicts()
{
- var parsedArgs = DefaultParse(new[] { "/win32res:foo", "/win32icon:foob", "a.cs" }, _baseDirectory);
+ var parsedArgs = DefaultParse(new[] { "/win32res:goo", "/win32icon:goob", "a.cs" }, _baseDirectory);
Assert.Equal(1, parsedArgs.Errors.Length);
Assert.Equal((int)ErrorCode.ERR_CantHaveWin32ResAndIcon, parsedArgs.Errors.First().Code);
- parsedArgs = DefaultParse(new[] { "/win32res:foo", "/win32manifest:foob", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { "/win32res:goo", "/win32manifest:goob", "a.cs" }, _baseDirectory);
Assert.Equal(1, parsedArgs.Errors.Length);
Assert.Equal((int)ErrorCode.ERR_CantHaveWin32ResAndManifest, parsedArgs.Errors.First().Code);
@@ -456,7 +458,7 @@ public void Win32ResConflicts()
Assert.Equal((int)ErrorCode.ERR_NoFileSpec, parsedArgs.Errors.First().Code);
Assert.Equal(1, parsedArgs.Errors.First().Arguments.Count);
- parsedArgs = DefaultParse(new[] { "/win32Manifest:foo", "/noWin32Manifest", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { "/win32Manifest:goo", "/noWin32Manifest", "a.cs" }, _baseDirectory);
Assert.Equal(0, parsedArgs.Errors.Length);
Assert.True(parsedArgs.NoWin32Manifest);
Assert.Equal(null, parsedArgs.Win32Manifest);
@@ -531,74 +533,74 @@ public void ParseResources()
{
var diags = new List();
- ResourceDescription desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar", _baseDirectory, diags, embedded: false);
+ ResourceDescription desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar", _baseDirectory, diags, embedded: false);
Assert.Equal(0, diags.Count);
- Assert.Equal(@"someFile.foo.bar", desc.FileName);
- Assert.Equal("someFile.foo.bar", desc.ResourceName);
+ Assert.Equal(@"someFile.goo.bar", desc.FileName);
+ Assert.Equal("someFile.goo.bar", desc.ResourceName);
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar,someName", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,someName", _baseDirectory, diags, embedded: false);
Assert.Equal(0, diags.Count);
- Assert.Equal(@"someFile.foo.bar", desc.FileName);
+ Assert.Equal(@"someFile.goo.bar", desc.FileName);
Assert.Equal("someName", desc.ResourceName);
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\s""ome Fil""e.foo.bar,someName", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\s""ome Fil""e.goo.bar,someName", _baseDirectory, diags, embedded: false);
Assert.Equal(0, diags.Count);
- Assert.Equal(@"some File.foo.bar", desc.FileName);
+ Assert.Equal(@"some File.goo.bar", desc.FileName);
Assert.Equal("someName", desc.ResourceName);
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar,""some Name"",public", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,""some Name"",public", _baseDirectory, diags, embedded: false);
Assert.Equal(0, diags.Count);
- Assert.Equal(@"someFile.foo.bar", desc.FileName);
+ Assert.Equal(@"someFile.goo.bar", desc.FileName);
Assert.Equal("some Name", desc.ResourceName);
Assert.True(desc.IsPublic);
// Use file name in place of missing resource name.
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar,,private", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded: false);
Assert.Equal(0, diags.Count);
- Assert.Equal(@"someFile.foo.bar", desc.FileName);
- Assert.Equal("someFile.foo.bar", desc.ResourceName);
+ Assert.Equal(@"someFile.goo.bar", desc.FileName);
+ Assert.Equal("someFile.goo.bar", desc.ResourceName);
Assert.False(desc.IsPublic);
// Quoted accessibility is fine.
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar,,""private""", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,""private""", _baseDirectory, diags, embedded: false);
Assert.Equal(0, diags.Count);
- Assert.Equal(@"someFile.foo.bar", desc.FileName);
- Assert.Equal("someFile.foo.bar", desc.ResourceName);
+ Assert.Equal(@"someFile.goo.bar", desc.FileName);
+ Assert.Equal("someFile.goo.bar", desc.ResourceName);
Assert.False(desc.IsPublic);
// Leading commas are not ignored...
- desc = CSharpCommandLineParser.ParseResourceDescription("", @",,\somepath\someFile.foo.bar,,private", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @",,\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded: false);
diags.Verify(
- // error CS1906: Invalid option '\somepath\someFile.foo.bar'; Resource visibility must be either 'public' or 'private'
- Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(@"\somepath\someFile.foo.bar"));
+ // error CS1906: Invalid option '\somepath\someFile.goo.bar'; Resource visibility must be either 'public' or 'private'
+ Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(@"\somepath\someFile.goo.bar"));
diags.Clear();
Assert.Null(desc);
// ...even if there's whitespace between them.
- desc = CSharpCommandLineParser.ParseResourceDescription("", @", ,\somepath\someFile.foo.bar,,private", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @", ,\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded: false);
diags.Verify(
- // error CS1906: Invalid option '\somepath\someFile.foo.bar'; Resource visibility must be either 'public' or 'private'
- Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(@"\somepath\someFile.foo.bar"));
+ // error CS1906: Invalid option '\somepath\someFile.goo.bar'; Resource visibility must be either 'public' or 'private'
+ Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments(@"\somepath\someFile.goo.bar"));
diags.Clear();
Assert.Null(desc);
// Trailing commas are ignored...
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar,,private", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private", _baseDirectory, diags, embedded: false);
diags.Verify();
diags.Clear();
- Assert.Equal("someFile.foo.bar", desc.FileName);
- Assert.Equal("someFile.foo.bar", desc.ResourceName);
+ Assert.Equal("someFile.goo.bar", desc.FileName);
+ Assert.Equal("someFile.goo.bar", desc.ResourceName);
Assert.False(desc.IsPublic);
// ...even if there's whitespace between them.
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar,,private, ,", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,,private, ,", _baseDirectory, diags, embedded: false);
diags.Verify();
diags.Clear();
- Assert.Equal("someFile.foo.bar", desc.FileName);
- Assert.Equal("someFile.foo.bar", desc.ResourceName);
+ Assert.Equal("someFile.goo.bar", desc.FileName);
+ Assert.Equal("someFile.goo.bar", desc.ResourceName);
Assert.False(desc.IsPublic);
- desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.foo.bar,someName,publi", _baseDirectory, diags, embedded: false);
+ desc = CSharpCommandLineParser.ParseResourceDescription("", @"\somepath\someFile.goo.bar,someName,publi", _baseDirectory, diags, embedded: false);
diags.Verify(Diagnostic(ErrorCode.ERR_BadResourceVis).WithArguments("publi"));
Assert.Null(desc);
diags.Clear();
@@ -1172,10 +1174,10 @@ public void ArgumentParsing()
Assert.False(parsedArgs.DisplayHelp);
Assert.True(parsedArgs.SourceFiles.Any());
- parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new[] { "/define:foo", "c.csx" }, _baseDirectory, s_defaultSdkDirectory);
+ parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new[] { "/define:goo", "c.csx" }, _baseDirectory, s_defaultSdkDirectory);
parsedArgs.Errors.Verify(
- // error CS2007: Unrecognized option: '/define:foo'
- Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/define:foo"));
+ // error CS2007: Unrecognized option: '/define:goo'
+ Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/define:goo"));
Assert.False(parsedArgs.DisplayHelp);
Assert.True(parsedArgs.SourceFiles.Any());
@@ -1452,21 +1454,21 @@ public void Define()
Assert.Equal(0, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
Assert.False(parsedArgs.Errors.Any());
- parsedArgs = DefaultParse(new[] { "/d:FOO", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { "/d:GOO", "a.cs" }, _baseDirectory);
Assert.Equal(1, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
- Assert.Contains("FOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
+ Assert.Contains("GOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
Assert.False(parsedArgs.Errors.Any());
- parsedArgs = DefaultParse(new[] { "/d:FOO;BAR,ZIP", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { "/d:GOO;BAR,ZIP", "a.cs" }, _baseDirectory);
Assert.Equal(3, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
- Assert.Contains("FOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
+ Assert.Contains("GOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
Assert.Contains("BAR", parsedArgs.ParseOptions.PreprocessorSymbolNames);
Assert.Contains("ZIP", parsedArgs.ParseOptions.PreprocessorSymbolNames);
Assert.False(parsedArgs.Errors.Any());
- parsedArgs = DefaultParse(new[] { "/d:FOO;4X", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { "/d:GOO;4X", "a.cs" }, _baseDirectory);
Assert.Equal(1, parsedArgs.ParseOptions.PreprocessorSymbolNames.Count());
- Assert.Contains("FOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
+ Assert.Contains("GOO", parsedArgs.ParseOptions.PreprocessorSymbolNames);
Assert.Equal(1, parsedArgs.Errors.Length);
Assert.Equal((int)ErrorCode.WRN_DefineIdentifierRequired, parsedArgs.Errors.First().Code);
Assert.Equal("4X", parsedArgs.Errors.First().Arguments[0]);
@@ -1898,18 +1900,14 @@ public void Embed()
parsedArgs = DefaultParse(new[] { "/embed:a.txt", "/debug-", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
- // These should fail when native PDB support is added.
parsedArgs = DefaultParse(new[] { "/embed", "/debug:full", "a.cs" }, _baseDirectory);
- parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
-
- parsedArgs = DefaultParse(new[] { "/embed", "/debug:full", "a.cs" }, _baseDirectory);
- parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
+ parsedArgs.Errors.Verify();
parsedArgs = DefaultParse(new[] { "/embed", "/debug:pdbonly", "a.cs" }, _baseDirectory);
- parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
+ parsedArgs.Errors.Verify();
parsedArgs = DefaultParse(new[] { "/embed", "/debug+", "a.cs" }, _baseDirectory);
- parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_CannotEmbedWithoutPdb));
+ parsedArgs.Errors.Verify();
}
[Theory]
@@ -1921,7 +1919,7 @@ public void Embed()
[InlineData("/debug:embedded", "/embed:embed.cs", new[] { "embed.cs", "embed.xyz" })]
[InlineData("/debug:embedded", "/embed:embed2.cs", new[] { "embed2.cs" })]
[InlineData("/debug:embedded", "/embed:embed.xyz", new[] {"embed.xyz" })]
- public void Embed_EndToEnd(string debugSwitch, string embedSwitch, string[] expectedEmbedded)
+ public void Embed_EndToEnd_Portable(string debugSwitch, string embedSwitch, string[] expectedEmbedded)
{
// embed.cs: large enough to compress, has #line directives
const string embed_cs =
@@ -1981,14 +1979,32 @@ static void Main() {
int exitCode = csc.Run(output);
Assert.Equal("", output.ToString().Trim());
Assert.Equal(0, exitCode);
-
- bool embedded = debugSwitch == "/debug:embedded";
+
+ switch (debugSwitch)
+ {
+ case "/debug:embedded":
+ ValidateEmbeddedSources_Portable(expectedEmbeddedMap, dir, isEmbeddedPdb: true);
+ break;
+ case "/debug:portable":
+ ValidateEmbeddedSources_Portable(expectedEmbeddedMap, dir, isEmbeddedPdb: false);
+ break;
+ case "/debug:full":
+ ValidateEmbeddedSources_Windows(expectedEmbeddedMap, dir);
+ break;
+ }
+
+ Assert.Empty(expectedEmbeddedMap);
+ CleanupAllGeneratedFiles(src.Path);
+ }
+
+ private static void ValidateEmbeddedSources_Portable(Dictionary expectedEmbeddedMap, TempDirectory dir, bool isEmbeddedPdb)
+ {
using (var peReader = new PEReader(File.OpenRead(Path.Combine(dir.Path, "embed.exe"))))
{
var entry = peReader.ReadDebugDirectory().SingleOrDefault(e => e.Type == DebugDirectoryEntryType.EmbeddedPortablePdb);
- Assert.Equal(embedded, entry.DataSize > 0);
+ Assert.Equal(isEmbeddedPdb, entry.DataSize > 0);
- using (var mdProvider = embedded ?
+ using (var mdProvider = isEmbeddedPdb ?
peReader.ReadEmbeddedPortablePdbDebugDirectoryData(entry) :
MetadataReaderProvider.FromPortablePdbStream(File.OpenRead(Path.Combine(dir.Path, "embed.pdb"))))
{
@@ -2011,9 +2027,36 @@ static void Main() {
}
}
}
+ }
- Assert.Empty(expectedEmbeddedMap);
- CleanupAllGeneratedFiles(src.Path);
+ private static void ValidateEmbeddedSources_Windows(Dictionary expectedEmbeddedMap, TempDirectory dir)
+ {
+ ISymUnmanagedReader5 symReader = null;
+
+ try
+ {
+ symReader = SymReaderFactory.CreateReader(File.OpenRead(Path.Combine(dir.Path, "embed.pdb")));
+
+ foreach (var doc in symReader.GetDocuments())
+ {
+ var docPath = doc.GetName();
+
+ var sourceBlob = doc.GetEmbeddedSource();
+ if (sourceBlob.Array == null)
+ {
+ continue;
+ }
+
+ var sourceStr = Encoding.UTF8.GetString(sourceBlob.Array, sourceBlob.Offset, sourceBlob.Count);
+
+ Assert.Equal(expectedEmbeddedMap[docPath], sourceStr);
+ Assert.True(expectedEmbeddedMap.Remove(docPath));
+ }
+ }
+ catch
+ {
+ symReader?.Dispose();
+ }
}
[Fact]
@@ -2093,51 +2136,51 @@ public void Deterministic()
[Fact]
public void ParseReferences()
{
- var parsedArgs = DefaultParse(new string[] { "/r:foo.dll", "a.cs" }, _baseDirectory);
+ var parsedArgs = DefaultParse(new string[] { "/r:goo.dll", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(2, parsedArgs.MetadataReferences.Length);
- parsedArgs = DefaultParse(new string[] { "/r:foo.dll;", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new string[] { "/r:goo.dll;", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(2, parsedArgs.MetadataReferences.Length);
Assert.Equal(typeof(object).Assembly.Location, parsedArgs.MetadataReferences[0].Reference);
Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);
- Assert.Equal("foo.dll", parsedArgs.MetadataReferences[1].Reference);
+ Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[1].Properties);
- parsedArgs = DefaultParse(new string[] { @"/l:foo.dll", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new string[] { @"/l:goo.dll", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(2, parsedArgs.MetadataReferences.Length);
Assert.Equal(typeof(object).Assembly.Location, parsedArgs.MetadataReferences[0].Reference);
Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);
- Assert.Equal("foo.dll", parsedArgs.MetadataReferences[1].Reference);
+ Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
Assert.Equal(MetadataReferenceProperties.Assembly.WithEmbedInteropTypes(true), parsedArgs.MetadataReferences[1].Properties);
- parsedArgs = DefaultParse(new string[] { @"/addmodule:foo.dll", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new string[] { @"/addmodule:goo.dll", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(2, parsedArgs.MetadataReferences.Length);
Assert.Equal(typeof(object).Assembly.Location, parsedArgs.MetadataReferences[0].Reference);
Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);
- Assert.Equal("foo.dll", parsedArgs.MetadataReferences[1].Reference);
+ Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
Assert.Equal(MetadataReferenceProperties.Module, parsedArgs.MetadataReferences[1].Properties);
- parsedArgs = DefaultParse(new string[] { @"/r:a=foo.dll", "/l:b=bar.dll", "/addmodule:c=mod.dll", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new string[] { @"/r:a=goo.dll", "/l:b=bar.dll", "/addmodule:c=mod.dll", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(4, parsedArgs.MetadataReferences.Length);
Assert.Equal(typeof(object).Assembly.Location, parsedArgs.MetadataReferences[0].Reference);
Assert.Equal(MetadataReferenceProperties.Assembly, parsedArgs.MetadataReferences[0].Properties);
- Assert.Equal("foo.dll", parsedArgs.MetadataReferences[1].Reference);
+ Assert.Equal("goo.dll", parsedArgs.MetadataReferences[1].Reference);
Assert.Equal(MetadataReferenceProperties.Assembly.WithAliases(new[] { "a" }), parsedArgs.MetadataReferences[1].Properties);
Assert.Equal("bar.dll", parsedArgs.MetadataReferences[2].Reference);
@@ -2152,25 +2195,25 @@ public void ParseReferences()
[Fact]
public void ParseAnalyzers()
{
- var parsedArgs = DefaultParse(new string[] { @"/a:foo.dll", "a.cs" }, _baseDirectory);
+ var parsedArgs = DefaultParse(new string[] { @"/a:goo.dll", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(1, parsedArgs.AnalyzerReferences.Length);
- Assert.Equal("foo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
+ Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
- parsedArgs = DefaultParse(new string[] { @"/analyzer:foo.dll", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new string[] { @"/analyzer:goo.dll", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(1, parsedArgs.AnalyzerReferences.Length);
- Assert.Equal("foo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
+ Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
- parsedArgs = DefaultParse(new string[] { "/analyzer:\"foo.dll\"", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new string[] { "/analyzer:\"goo.dll\"", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(1, parsedArgs.AnalyzerReferences.Length);
- Assert.Equal("foo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
+ Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
- parsedArgs = DefaultParse(new string[] { @"/a:foo.dll;bar.dll", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new string[] { @"/a:goo.dll;bar.dll", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(2, parsedArgs.AnalyzerReferences.Length);
- Assert.Equal("foo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
+ Assert.Equal("goo.dll", parsedArgs.AnalyzerReferences[0].FilePath);
Assert.Equal("bar.dll", parsedArgs.AnalyzerReferences[1].FilePath);
parsedArgs = DefaultParse(new string[] { @"/a:", "a.cs" }, _baseDirectory);
@@ -2826,33 +2869,33 @@ class C
{
public static void Main()
{
- Foo(0);
+ Goo(0);
#line 10 ""c:\temp\a\1.cs""
- Foo(1);
+ Goo(1);
#line 20 ""C:\a\..\b.cs""
- Foo(2);
+ Goo(2);
#line 30 ""C:\a\../B.cs""
- Foo(3);
+ Goo(3);
#line 40 ""../b.cs""
- Foo(4);
+ Goo(4);
#line 50 ""..\b.cs""
- Foo(5);
+ Goo(5);
#line 60 ""C:\X.cs""
- Foo(6);
+ Goo(6);
#line 70 ""C:\x.cs""
- Foo(7);
+ Goo(7);
#line 90 "" ""
- Foo(9);
+ Goo(9);
#line 100 ""C:\*.cs""
- Foo(10);
+ Goo(10);
#line 110 """"
- Foo(11);
+ Goo(11);
#line hidden
- Foo(12);
+ Goo(12);
#line default
- Foo(13);
+ Goo(13);
#line 140 ""***""
- Foo(14);
+ Goo(14);
}
}
";
@@ -2866,20 +2909,20 @@ public static void Main()
// with /fullpaths off
string expected = @"
-a.cs(8,13): error CS0103: The name 'Foo' does not exist in the current context
-c:\temp\a\1.cs(10,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\b.cs(20,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\B.cs(30,13): error CS0103: The name 'Foo' does not exist in the current context
-" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(40,13): error CS0103: The name 'Foo' does not exist in the current context
-" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(50,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\X.cs(60,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\x.cs(70,13): error CS0103: The name 'Foo' does not exist in the current context
- (90,7): error CS0103: The name 'Foo' does not exist in the current context
-C:\*.cs(100,7): error CS0103: The name 'Foo' does not exist in the current context
-(110,7): error CS0103: The name 'Foo' does not exist in the current context
-(112,13): error CS0103: The name 'Foo' does not exist in the current context
-a.cs(32,13): error CS0103: The name 'Foo' does not exist in the current context
-***(140,13): error CS0103: The name 'Foo' does not exist in the current context";
+a.cs(8,13): error CS0103: The name 'Goo' does not exist in the current context
+c:\temp\a\1.cs(10,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\b.cs(20,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\B.cs(30,13): error CS0103: The name 'Goo' does not exist in the current context
+" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(40,13): error CS0103: The name 'Goo' does not exist in the current context
+" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(50,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\X.cs(60,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\x.cs(70,13): error CS0103: The name 'Goo' does not exist in the current context
+ (90,7): error CS0103: The name 'Goo' does not exist in the current context
+C:\*.cs(100,7): error CS0103: The name 'Goo' does not exist in the current context
+(110,7): error CS0103: The name 'Goo' does not exist in the current context
+(112,13): error CS0103: The name 'Goo' does not exist in the current context
+a.cs(32,13): error CS0103: The name 'Goo' does not exist in the current context
+***(140,13): error CS0103: The name 'Goo' does not exist in the current context";
AssertEx.Equal(
expected.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries),
@@ -2893,20 +2936,20 @@ public static void Main()
Assert.Equal(1, exitCode);
expected = @"
-" + Path.Combine(dir.Path, @"a.cs") + @"(8,13): error CS0103: The name 'Foo' does not exist in the current context
-c:\temp\a\1.cs(10,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\b.cs(20,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\B.cs(30,13): error CS0103: The name 'Foo' does not exist in the current context
-" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(40,13): error CS0103: The name 'Foo' does not exist in the current context
-" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(50,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\X.cs(60,13): error CS0103: The name 'Foo' does not exist in the current context
-C:\x.cs(70,13): error CS0103: The name 'Foo' does not exist in the current context
- (90,7): error CS0103: The name 'Foo' does not exist in the current context
-C:\*.cs(100,7): error CS0103: The name 'Foo' does not exist in the current context
-(110,7): error CS0103: The name 'Foo' does not exist in the current context
-(112,13): error CS0103: The name 'Foo' does not exist in the current context
-" + Path.Combine(dir.Path, @"a.cs") + @"(32,13): error CS0103: The name 'Foo' does not exist in the current context
-***(140,13): error CS0103: The name 'Foo' does not exist in the current context";
+" + Path.Combine(dir.Path, @"a.cs") + @"(8,13): error CS0103: The name 'Goo' does not exist in the current context
+c:\temp\a\1.cs(10,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\b.cs(20,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\B.cs(30,13): error CS0103: The name 'Goo' does not exist in the current context
+" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(40,13): error CS0103: The name 'Goo' does not exist in the current context
+" + Path.GetFullPath(Path.Combine(dir.Path, @"..\b.cs")) + @"(50,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\X.cs(60,13): error CS0103: The name 'Goo' does not exist in the current context
+C:\x.cs(70,13): error CS0103: The name 'Goo' does not exist in the current context
+ (90,7): error CS0103: The name 'Goo' does not exist in the current context
+C:\*.cs(100,7): error CS0103: The name 'Goo' does not exist in the current context
+(110,7): error CS0103: The name 'Goo' does not exist in the current context
+(112,13): error CS0103: The name 'Goo' does not exist in the current context
+" + Path.Combine(dir.Path, @"a.cs") + @"(32,13): error CS0103: The name 'Goo' does not exist in the current context
+***(140,13): error CS0103: The name 'Goo' does not exist in the current context";
AssertEx.Equal(
expected.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries),
@@ -3592,22 +3635,22 @@ public void ParseErrorLogAndOut()
[Fact]
public void ModuleAssemblyName()
{
- var parsedArgs = DefaultParse(new[] { @"/target:module", "/moduleassemblyname:foo", "a.cs" }, _baseDirectory);
+ var parsedArgs = DefaultParse(new[] { @"/target:module", "/moduleassemblyname:goo", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
- Assert.Equal("foo", parsedArgs.CompilationName);
+ Assert.Equal("goo", parsedArgs.CompilationName);
Assert.Equal("a.netmodule", parsedArgs.CompilationOptions.ModuleName);
- parsedArgs = DefaultParse(new[] { @"/target:library", "/moduleassemblyname:foo", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { @"/target:library", "/moduleassemblyname:goo", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
// error CS0734: The /moduleassemblyname option may only be specified when building a target type of 'module'
Diagnostic(ErrorCode.ERR_AssemblyNameOnNonModule));
- parsedArgs = DefaultParse(new[] { @"/target:exe", "/moduleassemblyname:foo", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { @"/target:exe", "/moduleassemblyname:goo", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
// error CS0734: The /moduleassemblyname option may only be specified when building a target type of 'module'
Diagnostic(ErrorCode.ERR_AssemblyNameOnNonModule));
- parsedArgs = DefaultParse(new[] { @"/target:winexe", "/moduleassemblyname:foo", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { @"/target:winexe", "/moduleassemblyname:goo", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
// error CS0734: The /moduleassemblyname option may only be specified when building a target type of 'module'
Diagnostic(ErrorCode.ERR_AssemblyNameOnNonModule));
@@ -3616,9 +3659,9 @@ public void ModuleAssemblyName()
[Fact]
public void ModuleName()
{
- var parsedArgs = DefaultParse(new[] { @"/target:module", "/modulename:foo", "a.cs" }, _baseDirectory);
+ var parsedArgs = DefaultParse(new[] { @"/target:module", "/modulename:goo", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
- Assert.Equal("foo", parsedArgs.CompilationOptions.ModuleName);
+ Assert.Equal("goo", parsedArgs.CompilationOptions.ModuleName);
parsedArgs = DefaultParse(new[] { @"/target:library", "/modulename:bar", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
@@ -3628,9 +3671,9 @@ public void ModuleName()
parsedArgs.Errors.Verify();
Assert.Equal("CommonLanguageRuntimeLibrary", parsedArgs.CompilationOptions.ModuleName);
- parsedArgs = DefaultParse(new[] { @"/target:winexe", "/modulename:foo", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { @"/target:winexe", "/modulename:goo", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
- Assert.Equal("foo", parsedArgs.CompilationOptions.ModuleName);
+ Assert.Equal("goo", parsedArgs.CompilationOptions.ModuleName);
parsedArgs = DefaultParse(new[] { @"/target:exe", "/modulename:", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
@@ -3998,17 +4041,17 @@ public void Usings()
{
CSharpCommandLineArguments parsedArgs;
- parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new string[] { "/u:Foo.Bar" }, _baseDirectory, s_defaultSdkDirectory);
+ parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new string[] { "/u:Goo.Bar" }, _baseDirectory, s_defaultSdkDirectory);
parsedArgs.Errors.Verify();
- AssertEx.Equal(new[] { "Foo.Bar" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());
+ AssertEx.Equal(new[] { "Goo.Bar" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());
- parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new string[] { "/u:Foo.Bar;Baz", "/using:System.Core;System" }, _baseDirectory, s_defaultSdkDirectory);
+ parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new string[] { "/u:Goo.Bar;Baz", "/using:System.Core;System" }, _baseDirectory, s_defaultSdkDirectory);
parsedArgs.Errors.Verify();
- AssertEx.Equal(new[] { "Foo.Bar", "Baz", "System.Core", "System" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());
+ AssertEx.Equal(new[] { "Goo.Bar", "Baz", "System.Core", "System" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());
- parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new string[] { "/u:Foo;;Bar" }, _baseDirectory, s_defaultSdkDirectory);
+ parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new string[] { "/u:Goo;;Bar" }, _baseDirectory, s_defaultSdkDirectory);
parsedArgs.Errors.Verify();
- AssertEx.Equal(new[] { "Foo", "Bar" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());
+ AssertEx.Equal(new[] { "Goo", "Bar" }, parsedArgs.CompilationOptions.Usings.AsEnumerable());
parsedArgs = CSharpCommandLineParser.ScriptRunner.Parse(new string[] { "/u:" }, _baseDirectory, s_defaultSdkDirectory);
parsedArgs.Errors.Verify(
@@ -4747,10 +4790,10 @@ public void KeyContainerAndKeyFile()
Assert.Null(parsedArgs.CompilationOptions.CryptoKeyContainer);
// KEYFILE
- parsedArgs = DefaultParse(new[] { @"/keyfile:\somepath\s""ome Fil""e.foo.bar", "a.cs" }, _baseDirectory);
+ parsedArgs = DefaultParse(new[] { @"/keyfile:\somepath\s""ome Fil""e.goo.bar", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
//EDMAURER let's not set the option in the event that there was an error.
- //Assert.Equal(@"\somepath\some File.foo.bar", parsedArgs.CompilationOptions.CryptoKeyFile);
+ //Assert.Equal(@"\somepath\some File.goo.bar", parsedArgs.CompilationOptions.CryptoKeyFile);
parsedArgs = DefaultParse(new[] { "/keyFile", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(
@@ -5100,10 +5143,10 @@ public void ResponseFileSplitting()
// Quoting inside argument is valid.
responseFile = new string[] {
- @" /o:""foo.cs"" /o:""abc def""\baz ""/o:baz bar""bing",
+ @" /o:""goo.cs"" /o:""abc def""\baz ""/o:baz bar""bing",
};
args = CSharpCommandLineParser.ParseResponseLines(responseFile);
- AssertEx.Equal(new[] { @"/o:""foo.cs""", @"/o:""abc def""\baz", @"""/o:baz bar""bing" }, args);
+ AssertEx.Equal(new[] { @"/o:""goo.cs""", @"/o:""abc def""\baz", @"""/o:baz bar""bing" }, args);
}
[ConditionalFact(typeof(WindowsOnly))]
@@ -7016,11 +7059,11 @@ static void Main() { }
var fileName = Path.GetFileName(source);
var outWriter = new StringWriter(CultureInfo.InvariantCulture);
- int exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/target:library", "/out:foo.dll", "/nowarn:2008" }).Run(outWriter);
+ int exitCode = new MockCSharpCompiler(null, _baseDirectory, new[] { "/nologo", "/target:library", "/out:goo.dll", "/nowarn:2008" }).Run(outWriter);
Assert.Equal(0, exitCode);
Assert.Equal("", outWriter.ToString().Trim());
- System.IO.File.Delete(System.IO.Path.Combine(baseDir, "foo.dll"));
+ System.IO.File.Delete(System.IO.Path.Combine(baseDir, "goo.dll"));
CleanupAllGeneratedFiles(source);
}
@@ -7721,14 +7764,14 @@ private static void TestCS2002(string[] commandLineArgs, string baseDirectory, i
[Fact]
public void ErrorLineEnd()
{
- var tree = SyntaxFactory.ParseSyntaxTree("class C public { }", path: "foo");
+ var tree = SyntaxFactory.ParseSyntaxTree("class C public { }", path: "goo");
var comp = new MockCSharpCompiler(null, _baseDirectory, new[] { "/errorendlocation" });
var loc = new SourceLocation(tree.GetCompilationUnitRoot().FindToken(6));
var diag = new CSDiagnostic(new DiagnosticInfo(MessageProvider.Instance, (int)ErrorCode.ERR_MetadataNameTooLong), loc);
var text = comp.DiagnosticFormatter.Format(diag);
- string stringStart = "foo(1,7,1,8)";
+ string stringStart = "goo(1,7,1,8)";
Assert.Equal(stringStart, text.Substring(0, stringStart.Length));
}
@@ -7852,12 +7895,12 @@ public void ErrorPathsFromLineDirectives()
Assert.True(text.StartsWith(".>", StringComparison.Ordinal));
sampleProgram = @"
-#line 10 ""http://foo.bar/baz.aspx"" //URI
+#line 10 ""http://goo.bar/baz.aspx"" //URI
using System*
";
syntaxTree = SyntaxFactory.ParseSyntaxTree(sampleProgram, path: "filename.cs");
text = comp.DiagnosticFormatter.Format(syntaxTree.GetDiagnostics().First());
- Assert.True(text.StartsWith("http://foo.bar/baz.aspx", StringComparison.Ordinal));
+ Assert.True(text.StartsWith("http://goo.bar/baz.aspx", StringComparison.Ordinal));
}
[WorkItem(1119609, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1119609")]
diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs
index f95e768cb194a..1bfecc7610988 100644
--- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs
@@ -110,11 +110,11 @@ public static void Main() {}
public class Derived: Base
{
internal const string Str = ""temp"";
- public override int Foo { get { return 1; } }
+ public override int Goo { get { return 1; } }
}
public class Base
{
- public virtual int Foo { get { return 0; } }
+ public virtual int Goo { get { return 0; } }
}
";
CompileAndVerify(source);
@@ -221,8 +221,8 @@ public void TestAttributesOnClassWithConstantDefinedInClass()
{
var compilation = CreateStandardCompilation(@"
using System;
-[Attr(Foo.p)]
-class Foo
+[Attr(Goo.p)]
+class Goo
{
private const object p = null;
}
@@ -235,7 +235,7 @@ class C
public static void Main() { }
}
");
- var attrs = compilation.SourceModule.GlobalNamespace.GetMember("Foo").GetAttributes();
+ var attrs = compilation.SourceModule.GlobalNamespace.GetMember("Goo").GetAttributes();
Assert.Equal(1, attrs.Length);
attrs.First().VerifyValue
@@ -60,4 +61,4 @@
-
\ No newline at end of file
+
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs
index 55f12adb9a61f..3efae4bf8274c 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs
@@ -589,10 +589,10 @@ static void Main(string[] args)
}
static async Task Bar()
{
- NotImplementedException ex = await Foo();
+ NotImplementedException ex = await Goo();
return 3;
}
- public static async Task Foo() where T : Exception
+ public static async Task Goo() where T : Exception
{
Task task = null;
if (task != null) await task;
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs
index 20c6e7dc0cafb..8424a8c7c61f5 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs
@@ -378,10 +378,10 @@ struct TestCase
{
public async Task Run()
{
- return await Foo();
+ return await Goo();
}
- public async Task Foo()
+ public async Task Goo()
{
return await Task.Factory.StartNew(() => 42);
}
@@ -416,10 +416,10 @@ struct TestCase
{
public IEnumerable Run()
{
- yield return Foo();
+ yield return Goo();
}
- public int Foo()
+ public int Goo()
{
return 42;
}
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs
index 7a70a9b0cf810..d0e53ddd40daa 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs
@@ -1556,14 +1556,14 @@ public async void Run()
try
{
MyStruct ms = new MyStruct();
- var x = ms[index: await Foo()];
+ var x = ms[index: await Goo()];
}
finally
{
Driver.CompletedSignal.Set();
}
}
- public async Task Foo()
+ public async Task Goo()
{
await Task.Delay(1);
return 1;
@@ -1847,7 +1847,7 @@ public async void Run()
int[][] arr2 = new[]
{
new []{await GetVal(2),await GetVal(3)},
- await Foo()
+ await Goo()
};
if (arr2[0][1] == 3 && arr2[1][1] == 2)
Driver.Count++;
@@ -1860,7 +1860,7 @@ await Foo()
}
}
- public async Task Foo()
+ public async Task Goo()
{
await Task.Delay(1);
return new int[] { 1, 2, 3 };
@@ -1994,7 +1994,7 @@ public async void Run()
dynamic arr2 = new[]
{
new []{await GetVal(2),3},
- await Foo()
+ await Goo()
};
if (arr2[0][1] == 3 && arr2[1][1] == 2)
Driver.Count++;
@@ -2007,7 +2007,7 @@ await Foo()
}
}
- public async Task Foo()
+ public async Task Goo()
{
await Task.Delay(1);
return new int[] { 1, 2, 3 };
@@ -2337,7 +2337,7 @@ public void SpillWithByRefArguments01()
class BaseTestCase
{
- public void FooRef(ref decimal d, int x, out decimal od)
+ public void GooRef(ref decimal d, int x, out decimal od)
{
od = d;
d++;
@@ -2361,7 +2361,7 @@ public async void Run()
decimal od;
tests++;
- base.FooRef(ref d, await base.GetVal(4), out od);
+ base.GooRef(ref d, await base.GetVal(4), out od);
if (d == 2 && od == 1) Driver.Count++;
}
finally
@@ -2513,15 +2513,15 @@ public void Async_StackSpill_Argument_Generic04()
using System.Threading.Tasks;
public class mc
{
- async public System.Threading.Tasks.Task Foo(T t, V u) { await Task.Delay(1); return u; }
+ async public System.Threading.Tasks.Task Goo(T t, V u) { await Task.Delay(1); return u; }
}
class Test
{
- static async Task Foo()
+ static async Task Goo()
{
dynamic mc = new mc();
- var rez = await mc.Foo(null, await ((Func>)(async () => { await Task.Delay(1); return ""Test""; }))());
+ var rez = await mc.Goo(null, await ((Func>)(async () => { await Task.Delay(1); return ""Test""; }))());
if (rez == ""Test"")
return 0;
return 1;
@@ -2529,7 +2529,7 @@ static async Task Foo()
static void Main()
{
- Console.WriteLine(Foo().Result);
+ Console.WriteLine(Goo().Result);
}
}";
CompileAndVerify(source, "0", references: new[] { CSharpRef });
@@ -2649,7 +2649,7 @@ await GetValue(2)
}
}
- public int Foo { get; set; }
+ public int Goo { get; set; }
}
class Driver
@@ -2686,14 +2686,14 @@ class MyClass
class TestCase
{
- public static int Foo(ref int x, int y)
+ public static int Goo(ref int x, int y)
{
return x + y;
}
public async Task Run()
{
- return Foo(
+ return Goo(
ref (new MyClass() { Field = 21 }.Field),
await Task.Factory.StartNew(() => 21));
}
@@ -2853,12 +2853,12 @@ struct s1
{
public int X;
- public async void Foo1()
+ public async void Goo1()
{
Bar(ref this, await Task.FromResult(42));
}
- public void Foo2()
+ public void Goo2()
{
Bar(ref this, 42);
}
@@ -2873,12 +2873,12 @@ class c1
{
public int X;
- public async void Foo1()
+ public async void Goo1()
{
Bar(this, await Task.FromResult(42));
}
- public void Foo2()
+ public void Goo2()
{
Bar(this, 42);
}
@@ -2896,28 +2896,28 @@ public static void Main()
{
s1 s;
s.X = -1;
- s.Foo1();
+ s.Goo1();
Console.WriteLine(s.X);
}
{
s1 s;
s.X = -1;
- s.Foo2();
+ s.Goo2();
Console.WriteLine(s.X);
}
{
c1 c = new c1();
c.X = -1;
- c.Foo1();
+ c.Goo1();
Console.WriteLine(c.X);
}
{
c1 c = new c1();
c.X = -1;
- c.Foo2();
+ c.Goo2();
Console.WriteLine(c.X);
}
}
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
index 291b70af9729a..ed9869a023015 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
@@ -337,7 +337,7 @@ public void Conformance_Awaiting_Methods_Parameter003()
class TestCase
{
public static int Count = 0;
- public static T Foo(T t)
+ public static T Goo(T t)
{
return t;
}
@@ -352,9 +352,9 @@ public static async void Run()
{
try
{
- int x1 = Foo(await Bar(4));
+ int x1 = Goo(await Bar(4));
Task t = Bar(5);
- int x2 = Foo(await t);
+ int x2 = Goo(await t);
if (x1 != 4)
Count++;
if (x2 != 5)
@@ -879,7 +879,7 @@ public void Inference()
struct Test
{
- public Task Foo
+ public Task Goo
{
get { return Task.Run(async () => { await Task.Delay(1); return ""abc""; }); }
}
@@ -905,12 +905,12 @@ public async void Run()
Test t = new Test();
tests++;
- var x1 = await TestCase.GetValue(await t.Foo);
+ var x1 = await TestCase.GetValue(await t.Goo);
if (x1 == ""abc"")
Driver.Count++;
tests++;
- var x2 = await TestCase.GetValue1(t.Foo);
+ var x2 = await TestCase.GetValue1(t.Goo);
if (x2 == ""abc"")
Driver.Count++;
@@ -955,8 +955,8 @@ class TestCase
public async void Run()
{
int tests = 0;
- var x1 = ((await Foo1()) is object);
- var x2 = ((await Foo2()) as string);
+ var x1 = ((await Goo1()) is object);
+ var x2 = ((await Goo2()) as string);
if (x1 == true)
tests++;
if (x2 == ""string"")
@@ -966,7 +966,7 @@ public async void Run()
Driver.CompletedSignal.Set();
}
- public async Task Foo1()
+ public async Task Goo1()
{
await Task.Delay(1);
TestCase.Count++;
@@ -974,7 +974,7 @@ public async Task Foo1()
return i;
}
- public async Task