From c7d122d917b4d8ace9b124dc504402491ba05d45 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Thu, 24 May 2018 14:57:43 -0700 Subject: [PATCH 01/12] VSTS YAML file Simple YAML file to start playing around with VSTS CI here. --- .vsts-dotnet.yml | 98 ++++++++++++++++++++++++++++++++++++ build/Targets/Settings.props | 5 +- build/scripts/build.ps1 | 2 +- 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 .vsts-dotnet.yml diff --git a/.vsts-dotnet.yml b/.vsts-dotnet.yml new file mode 100644 index 0000000000000..e3b33848b9607 --- /dev/null +++ b/.vsts-dotnet.yml @@ -0,0 +1,98 @@ +phases: +- phase: Windows_Desktop_Unit_Tests + queue: + name: Helix + timeoutInMinutes: 90 + parallel: 4 + matrix: + debug_32: + _configuration: Debug + _testKind: Test32 + debug_64: + _configuration: Debug + _testKind: Test64 + release_32: + _configuration: Release + _testKind: Test32 + release_64: + _configuration: Release + _testKind: Test64 + + steps: + - script: build/scripts/cibuild.cmd -$(_configuration) -testDesktop -$(_testKind) + + - task: PublishTestResults@1 + inputs: + testRunner: XUnit + testResultsFiles: '**/xUnitResults/*.xml' + mergeTestResults: true + testRunTitle: 'Windows Desktop $(_configuration) $(_testKind)' + condition: succeededOrFailed() + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.SourcesDirectory)\Binaries\$(_configuration)\Logs' + ArtifactName: 'Windows Desktop $(_configuration) $(_testKind)' + publishLocation: Container + continueOnError: true + condition: failed() + +- phase: Windows_CoreClr_Unit_Tests + queue: + name: Helix + timeoutInMinutes: 90 + parallel: 2 + matrix: + debug: + _configuration: Debug + release: + _configuration: Release + + steps: + - script: build/scripts/cibuild.cmd -$(_configuration) -testCoreClr -buildCoreClr + + - task: PublishTestResults@1 + inputs: + testRunner: XUnit + testResultsFiles: '**/xUnitResults/*.xml' + mergeTestResults: true + testRunTitle: 'Windows CoreClr $(_configuration)' + condition: succeededOrFailed() + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.SourcesDirectory)\Binaries\$(_configuration)\Logs' + ArtifactName: 'Windows CoreClr $(_configuration)' + publishLocation: Container + continueOnError: true + condition: failed() + +- phase: Windows_Determinism_Test + queue: + name: Helix + timeoutInMinutes: 90 + steps: + - script: build/scripts/cibuild.cmd -testDeterminism + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.SourcesDirectory)\Binaries\Debug\Logs' + ArtifactName: 'Build Determinism Files' + publishLocation: Container + continueOnError: true + condition: failed() + +- phase: Windows_Correctness_Test + queue: + name: Helix + timeoutInMinutes: 90 + steps: + - script: build/scripts/test-build-correctness.cmd -cibuild -release + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.SourcesDirectory)\Binaries\Release\Logs' + ArtifactName: 'Build Correctness Files' + publishLocation: Container + continueOnError: true + condition: failed() diff --git a/build/Targets/Settings.props b/build/Targets/Settings.props index 4fcc6aadef6a6..d8462519692f7 100644 --- a/build/Targets/Settings.props +++ b/build/Targets/Settings.props @@ -24,8 +24,11 @@ false true + false + true false - true + true + false true $(RepoRoot)nuget.exe diff --git a/build/scripts/build.ps1 b/build/scripts/build.ps1 index 902dad6163131..78f6136baf793 100644 --- a/build/scripts/build.ps1 +++ b/build/scripts/build.ps1 @@ -367,7 +367,7 @@ function Build-InsertionItems() { } function Build-Installer () { - # Copying Artifacts + ## Copying Artifacts $installerDir = Join-Path $configDir "Installer" Create-Directory $installerDir From cc3e29e54c216b28c6c665f299b7987415cf989c Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 18 Jul 2018 10:51:44 -0700 Subject: [PATCH 02/12] Disable leak detection tests on x64 Looks like RyuJIT change causes our leak detection tests to fail. Disabling until we find a better pattern to use there. https://github.com/dotnet/roslyn/issues/28639 --- .../Test/Preview/PreviewWorkspaceTests.cs | 4 +++- .../ProjectCacheHostServiceFactoryTests.cs | 24 ++++++++++++------- .../MSBuildTest/MSBuildWorkspaceTests.cs | 3 ++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs index 59d2c07c79c00..c1bc280e3cd10 100644 --- a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs @@ -251,7 +251,9 @@ public async Task TestPreviewDiagnosticTaggerInPreviewPane() } } - [Fact, Trait(Traits.Editor, Traits.Editors.Preview)] + [Trait(Traits.Editor, Traits.Editors.Preview)] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestPreviewWorkspaceDoesNotLeakSolution() { // Verify that analyzer execution doesn't leak solution instances from the preview workspace. diff --git a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs index b70daf0535a74..8861dc22e729e 100644 --- a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs @@ -28,7 +28,8 @@ private void Test(Action @@ -46,7 +47,8 @@ public void TestCacheKeepsObjectAlive1() }); } - [Fact] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestCacheKeepsObjectAlive2() { Test((cacheService, projectId, owner, instance) => @@ -64,7 +66,8 @@ public void TestCacheKeepsObjectAlive2() }); } - [Fact] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected1() { Test((cacheService, projectId, owner, instance) => @@ -79,7 +82,8 @@ public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected1() }); } - [Fact] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected2() { Test((cacheService, projectId, owner, instance) => @@ -94,7 +98,8 @@ public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected2() }); } - [Fact] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestImplicitCacheKeepsObjectAlive1() { var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); @@ -127,7 +132,8 @@ private static ObjectReference PutObjectInImplicitCache(ProjectCacheServ return reference; } - [Fact] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestP2PReference() { var workspace = new AdhocWorkspace(); @@ -154,7 +160,8 @@ public void TestP2PReference() instanceTracker.AssertReleased(); } - [Fact] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestEjectFromImplicitCache() { List compilations = new List(); @@ -181,7 +188,8 @@ public void TestEjectFromImplicitCache() GC.KeepAlive(cache); } - [Fact] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] + [ConditionalFact(typeof(x86))] public void TestCacheCompilationTwice() { var comp1 = CSharpCompilation.Create("1"); diff --git a/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs index de62fa42460ad..bd1b0114cf9db 100644 --- a/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs @@ -3122,8 +3122,9 @@ public async Task MSBuildProjectShouldHandleDefaultCodePageProperty() } } - [ConditionalFact(typeof(VisualStudioMSBuildInstalled)), Trait(Traits.Feature, Traits.Features.MSBuildWorkspace)] + [ConditionalFact(typeof(VisualStudioMSBuildInstalled), typeof(x86)), Trait(Traits.Feature, Traits.Features.MSBuildWorkspace)] [WorkItem(981208, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/981208")] + [WorkItem(28639, "https://github.com/dotnet/roslyn/issues/28639")] public void DisposeMSBuildWorkspaceAndServicesCollected() { CreateFiles(GetSimpleCSharpSolutionFiles()); From 568cfe7faca2985a470765c7ee15b76f10b3787b Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 25 Jul 2018 08:38:07 -0700 Subject: [PATCH 03/12] Disable icacls use on Helix The icacls command is failing on Helix hence disabling these tests there for now Related to #28836 --- src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs | 8 ++++++++ .../VisualBasic/Test/CommandLine/CommandLineTests.vb | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 9776f11d866ef..d1c9a5b878007 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -4981,6 +4981,14 @@ public void Bug15538() { return; } + + // The icacls command fails on our Helix machines and it appears to be related to the use of the $ in + // the username. + // https://github.com/dotnet/roslyn/issues/28836 + if (StringComparer.OrdinalIgnoreCase.Equals(Environment.UserDomainName, "WORKGROUP")) + { + return; + } } var folder = Temp.CreateDirectory(); diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index ecd39e7d65ce7..1a4136e45af26 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -6362,6 +6362,13 @@ End Module Public Sub Bug15538() + ' The icacls command fails on our Helix machines And it appears to be related to the use of the $ in + ' the username. + ' https://github.com/dotnet/roslyn/issues/28836 + If StringComparer.OrdinalIgnoreCase.Equals(Environment.UserDomainName, "WORKGROUP") Then + Return + End If + Dim folder = Temp.CreateDirectory() Dim source As String = folder.CreateFile("src.vb").WriteAllText("").Path Dim ref As String = folder.CreateFile("ref.dll").WriteAllText("").Path From 466246bd34b968bc891a6eaf0a007607b761f39e Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 17 Jul 2018 12:33:58 -0700 Subject: [PATCH 04/12] Make mutex creation more robust The existing code for creating a Mutex for the current user failed when the user name included a `$`. The reasons for why it failed are unknown but verified using the WindowsIdentity instance directly accounts for this. --- .../Server/VBCSCompilerTests/DesktopBuildClientTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Server/VBCSCompilerTests/DesktopBuildClientTests.cs b/src/Compilers/Server/VBCSCompilerTests/DesktopBuildClientTests.cs index 9e8ba32286866..2224fd8a9110e 100644 --- a/src/Compilers/Server/VBCSCompilerTests/DesktopBuildClientTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/DesktopBuildClientTests.cs @@ -16,6 +16,7 @@ using System.Threading; using System.IO.Pipes; using System.Security.AccessControl; +using System.Security.Principal; namespace Microsoft.CodeAnalysis.CompilerServer.UnitTests { @@ -154,8 +155,7 @@ public void TestMutexConstructorException() { Assert.True(createdNew); var mutexSecurity = outer.GetAccessControl(); - var user = Environment.UserDomainName + "\\" + Environment.UserName; - mutexSecurity.AddAccessRule(new MutexAccessRule(user, MutexRights.FullControl, AccessControlType.Deny)); + mutexSecurity.AddAccessRule(new MutexAccessRule(WindowsIdentity.GetCurrent().Owner, MutexRights.FullControl, AccessControlType.Deny)); outer.SetAccessControl(mutexSecurity); var ranLocal = false; From e6333991660d5d6780914282d4a0133dc5e3dbc9 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Thu, 26 Jul 2018 09:49:21 -0700 Subject: [PATCH 05/12] Move to language version 8 The MSBuild workspaces tests needed to be updated to reflect that C# 8.0 is the new default. --- src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs index bd1b0114cf9db..9096090fee4f6 100644 --- a/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/MSBuildWorkspaceTests.cs @@ -1765,7 +1765,7 @@ public async Task TestParseOptions_CSharp_Compatibility_None() public async Task TestParseOptions_CSharp_LanguageVersion_Latest() { CreateCSharpFiles(); - await AssertCSParseOptionsAsync(CS.LanguageVersion.CSharp7, options => options.LanguageVersion); + await AssertCSParseOptionsAsync(CS.LanguageVersion.CSharp8, options => options.LanguageVersion); } [ConditionalFact(typeof(VisualStudioMSBuildInstalled)), Trait(Traits.Feature, Traits.Features.MSBuildWorkspace)] From 3df0f5d78195559f0c57300a5a3fd0f7c53aa410 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Thu, 26 Jul 2018 15:44:35 -0700 Subject: [PATCH 06/12] Make sure TLS 1.2 is used to fetch from https://dot.net https://github.com/dotnet/announcements/issues/77 for the change and where I am stealing this change from. --- build/scripts/build-utils.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/scripts/build-utils.ps1 b/build/scripts/build-utils.ps1 index e34d6c88d406e..f90a0fd0edf64 100644 --- a/build/scripts/build-utils.ps1 +++ b/build/scripts/build-utils.ps1 @@ -148,6 +148,7 @@ function Ensure-DotnetSdk() { Create-Directory $cliDir Create-Directory $toolsDir $destFile = Join-Path $toolsDir "dotnet-install.ps1" + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $webClient = New-Object -TypeName "System.Net.WebClient" $webClient.DownloadFile("https://dot.net/v1/dotnet-install.ps1", $destFile) Exec-Block { & $destFile -Version $sdkVersion -InstallDir $cliDir } | Out-Null From e0d21eb1d6f189402285a801fba6ab70e65024fc Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 31 Jul 2018 09:22:46 -0700 Subject: [PATCH 07/12] Port determinism fixes --- build/scripts/test-determinism.ps1 | 121 +++++++++++++++++++---------- 1 file changed, 79 insertions(+), 42 deletions(-) diff --git a/build/scripts/test-determinism.ps1 b/build/scripts/test-determinism.ps1 index cd5963864b609..17b1335a51e5b 100644 --- a/build/scripts/test-determinism.ps1 +++ b/build/scripts/test-determinism.ps1 @@ -1,6 +1,15 @@ +#### +# +# $bootstrapDir: directory containing the bootstrap compiler +# $release: whether to build a debug or release build +# $altRootDrive: the drive we build on (via subst) for verifying pathmap implementation +# +#### [CmdletBinding(PositionalBinding=$false)] param ( [string]$bootstrapDir = "", - [switch]$debugDeterminism = $false) + [switch]$release = $false, + [string]$altRootDrive = "q:") + Set-StrictMode -version 2.0 $ErrorActionPreference = "Stop" @@ -12,26 +21,25 @@ $ErrorActionPreference = "Stop" $script:skipList = @() # Location that deterministic error information should be written to. -[string]$script:errorDir = "" -[string]$script:errorDirLeft = "" -[string]$script:errorDirRight = "" +[string]$errorDir = "" +[string]$errorDirLeft = "" +[string]$errorDirRight = "" -function Run-Build([string]$rootDir, [switch]$restore = $false, [string]$logFile = $null) { +function Run-Build([string]$rootDir, [string]$logFile = $null) { Push-Location $rootDir try { # Clean out the previous run Write-Host "Cleaning the Binaries" - Exec-Console $msbuild "/nologo /v:m /nodeReuse:false /t:clean Roslyn.sln" + Remove-Item -Recurse (Get-ConfigDir $rootDir) + Remove-Item -Recurse (Get-ObjDir $rootDir) - if ($restore) { - Write-Host "Restoring the packages" - Restore-Project $dotnet "Roslyn.sln" - } + Write-Host "Restoring the packages" + Restore-Project $dotnet "Roslyn.sln" - $args = "/nologo /v:m /nodeReuse:false /m /p:DebugDeterminism=true /p:BootstrapBuildPath=$script:bootstrapDir /p:Features=`"debug-determinism`" /p:UseRoslynAnalyzers=false /p:DeployExtension=false Roslyn.sln" + $args = "/nologo /v:m /nodeReuse:false /m /p:DebugDeterminism=true /p:DeveloperBuild=false /p:BootstrapBuildPath=$script:bootstrapDir /p:Features=`"debug-determinism`" /p:UseRoslynAnalyzers=false /p:DeployExtension=false Roslyn.sln" if ($logFile -ne $null) { - $logFile = Join-Path $binariesDir $logFile + $logFile = Join-Path $logDir $logFile $args += " /bl:$logFile" } @@ -47,27 +55,45 @@ function Get-ObjDir([string]$rootDir) { return Join-Path $rootDir "Binaries\Obj" } +function Get-ConfigDir([string]$rootDir) { + return Join-Path $rootDir "Binaries\$buildConfiguration" +} + # Return all of the files that need to be processed for determinism under the given # directory. function Get-FilesToProcess([string]$rootDir) { $objDir = Get-ObjDir $rootDir - foreach ($item in Get-ChildItem -re -in *.dll,*.exe,*.pdb $objDir) { - $fileFullName = $item.FullName - $fileName = Split-Path -leaf $fileFullName + foreach ($item in Get-ChildItem -re -in *.dll,*.exe,*.pdb,*.sourcelink.json $objDir) { + $filePath = $item.FullName + $fileName = Split-Path -leaf $filePath if ($skipList.Contains($fileName)) { continue; } - $fileId = $fileFullName.Substring($objDir.Length).Replace("\", ".") - $fileHash = (Get-FileHash $fileFullName -algorithm MD5).Hash + $fileId = $filePath.Substring($objDir.Length).Replace("\", ".") + $fileHash = (Get-FileHash $filePath -algorithm MD5).Hash $data = @{} $data.Hash = $fileHash - $data.Content = [IO.File]::ReadAllBytes($fileFullName) + $data.Content = [IO.File]::ReadAllBytes($filePath) $data.FileId = $fileId $data.FileName = $fileName - $data.FileFullName = $fileFullName + $data.FilePath = $filePath + + $keyFilePath = $filePath + ".key" + $keyFileName = Split-Path -leaf $keyFilePath + if (Test-Path $keyFilePath) { + $data.KeyFileName = $keyFileName + $data.KeyFilePath = $keyFilePath + $data.KeyFileContent = [IO.File]::ReadAllBytes($keyFilePath) + } + else { + $data.KeyFileName = "" + $data.KeyFilePath = "" + $data.KeyFileContent = $null + } + Write-Output $data } } @@ -117,8 +143,8 @@ function Test-MapContents($dataMap) { } } -function Test-Build([string]$rootDir, $dataMap, [string]$logFile, [switch]$restore = $false) { - Run-Build $rootDir -logFile $logFile -restore:$restore +function Test-Build([string]$rootDir, $dataMap, [string]$logFile) { + Run-Build $rootDir -logFile $logFile $errorList = @() $allGood = $true @@ -127,10 +153,10 @@ function Test-Build([string]$rootDir, $dataMap, [string]$logFile, [switch]$resto foreach ($fileData in Get-FilesToProcess $rootDir) { $fileId = $fileData.FileId $fileName = $fileData.FileName - $fileFullName = $fileData.FileFullName + $filePath = $fileData.FilePath if (-not $dataMap.Contains($fileId)) { - Write-Host "ERROR! Missing entry in map $fileId->$fileFullName" + Write-Host "ERROR! Missing entry in map $fileId->$filePath" $allGood = $false continue } @@ -142,8 +168,16 @@ function Test-Build([string]$rootDir, $dataMap, [string]$logFile, [switch]$resto $errorList += $fileName # Save out the original and baseline so Jenkins will archive them for investigation - [IO.File]::WriteAllBytes((Join-Path $script:errorDirLeft $fileName), $oldFileData.Content) - Copy-Item $fileFullName (Join-Path $script:errorDirRight $fileName) + [IO.File]::WriteAllBytes((Join-Path $errorDirLeft $fileName), $oldFileData.Content) + Copy-Item $filePath (Join-Path $errorDirRight $fileName) + + # Copy the key files if available too + $keyFileName = $oldFileData.KeyFileName + if ($keyFileName -ne "") { + [IO.File]::WriteAllBytes((Join-Path $errorDirLeft $keyFileName), $oldFileData.KeyFileContent) + Copy-Item $fileData.KeyFilePath (Join-Path $errorDirRight $keyFileName) + } + continue } @@ -157,7 +191,7 @@ function Test-Build([string]$rootDir, $dataMap, [string]$logFile, [switch]$resto } Write-Host "Archiving failure information" - $zipFile = Join-Path $repoDir "Binaries\determinism.zip" + $zipFile = Join-Path $logDir "determinism.zip" Add-Type -Assembly "System.IO.Compression.FileSystem"; [System.IO.Compression.ZipFile]::CreateFromDirectory($script:errorDir, $zipFile, "Fastest", $true); @@ -169,14 +203,6 @@ function Test-Build([string]$rootDir, $dataMap, [string]$logFile, [switch]$resto function Run-Test() { $rootDir = $repoDir - # Ensure the error directory is written for all analysis to use. - $script:errorDir = Join-Path $repoDir "Binaries\Determinism" - $script:errorDirLeft = Join-Path $script:errorDir "Left" - $script:errorDirRight = Join-Path $script:errorDir "Right" - Create-Directory $script:errorDir - Create-Directory $script:errorDirLeft - Create-Directory $script:errorDirRight - # Run the initial build so that we can populate the maps Run-Build $repoDir -logFile "initial.binlog" $dataMap = Record-Binaries $repoDir @@ -188,19 +214,30 @@ function Run-Test() { # Run another build in a different source location and verify that path mapping # allows the build to be identical. To do this we'll copy the entire source # tree under the Binaries\q directory and run a build from there. - $altRootDir = Join-Path "$repoDir\Binaries" "q" - Remove-Item -re -fo $altRootDir -ErrorAction SilentlyContinue - & robocopy $repoDir $altRootDir /E /XD $binariesDir /XD ".git" /njh /njs /ndl /nc /ns /np /nfl - - # Symlink the .git directory to make SourceLink think Binaries/q is the repo root: - & cmd /c mklink /d (Join-Path $altRootDir ".git") (Join-Path $repoDir ".git") - - Test-Build -rootDir $altRootDir -dataMap $dataMap -logFile "test2.binlog" -restore + Write-Host "Building in a different directory" + Exec-Command "subst" "$altRootDrive $(Split-Path -parent $repoDir)" + try { + $altRootDir = Join-Path "$($altRootDrive)\" (Split-Path -leaf $repoDir) + Test-Build -rootDir $altRootDir -dataMap $dataMap -logFile "test2.binlog" + } + finally { + Exec-Command "subst" "$altRootDrive /d" + } } try { . (Join-Path $PSScriptRoot "build-utils.ps1") + # Create all of the logging directories + $buildConfiguration = if ($release) { "Release" } else { "Debug" } + $logDir = Join-Path (Get-ConfigDir $repoDir) "Logs" + $errorDir = Join-Path $binariesDir "Determinism" + $errorDirLeft = Join-Path $errorDir "Left" + $errorDirRight = Join-Path $errorDir "Right" + Create-Directory $logDir + Create-Directory $errorDirLeft + Create-Directory $errorDirRight + $dotnet = Ensure-DotnetSdk $msbuild = Ensure-MSBuild if (($bootstrapDir -eq "") -or (-not ([IO.Path]::IsPathRooted($script:bootstrapDir)))) { From 89147f4f90889c08af693bb9e44fcfc82f966656 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 1 Aug 2018 08:15:42 -0700 Subject: [PATCH 08/12] Migrate Linux tests to VSTS --- .vsts-dotnet.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.vsts-dotnet.yml b/.vsts-dotnet.yml index e3b33848b9607..fd93826ddc6a0 100644 --- a/.vsts-dotnet.yml +++ b/.vsts-dotnet.yml @@ -96,3 +96,26 @@ phases: publishLocation: Container continueOnError: true condition: failed() + +- phase: Linux_Test + queue: + name: DotNetCore-Linux + timeoutInMinutes: 90 + parallel: 2 + matrix: + coreclr: + _args: --debug + _name: CoreClr + mono: + _args: --debug --docker --mono + _name: Mono + steps: + - script: ./build/scripts/cibuild.sh $(_args) + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/Binaries/$(_configuration)/Logs' + ArtifactName: 'Linux $(_name)' + publishLocation: Container + continueOnError: true + condition: failed() + From 7489a9a2f7417f8aed7ad34990e0f067ce5cb4fd Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 1 Aug 2018 08:30:39 -0700 Subject: [PATCH 09/12] Force retest From a6c215fad900d0300bd45486f34a114b1c2e085b Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 1 Aug 2018 09:58:01 -0700 Subject: [PATCH 10/12] Publish Linux Test Results --- .vsts-dotnet.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.vsts-dotnet.yml b/.vsts-dotnet.yml index fd93826ddc6a0..32d2ba1f9e975 100644 --- a/.vsts-dotnet.yml +++ b/.vsts-dotnet.yml @@ -111,6 +111,14 @@ phases: _name: Mono steps: - script: ./build/scripts/cibuild.sh $(_args) + - task: PublishTestResults@1 + inputs: + testRunner: XUnit + testResultsFiles: '**/xUnitResults/*.xml' + mergeTestResults: true + testRunTitle: 'Linux $(_name)' + condition: succeededOrFailed() + - task: PublishBuildArtifacts@1 inputs: PathtoPublish: '$(Build.SourcesDirectory)/Binaries/$(_configuration)/Logs' From 14efbdeda42a86f423734970106a819672012e89 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 2 Aug 2018 07:46:54 -0700 Subject: [PATCH 11/12] Use struct for TypeSymbolWithAnnotations (#28943) --- ...spaceOrTypeOrAliasSymbolWithAnnotations.cs | 32 +- .../Portable/Binder/Binder.ValueChecks.cs | 4 +- .../Portable/Binder/Binder_Attributes.cs | 2 +- .../CSharp/Portable/Binder/Binder_Crefs.cs | 4 +- .../Portable/Binder/Binder_Deconstruct.cs | 8 +- .../Portable/Binder/Binder_Expressions.cs | 16 +- .../CSharp/Portable/Binder/Binder_Lambda.cs | 4 +- .../Portable/Binder/Binder_Operators.cs | 2 +- .../CSharp/Portable/Binder/Binder_Patterns.cs | 2 +- .../Portable/Binder/Binder_Statements.cs | 10 +- .../CSharp/Portable/Binder/Binder_Symbols.cs | 8 +- .../Portable/Binder/Binder_TupleOperators.cs | 2 +- .../Portable/Binder/ForEachEnumeratorInfo.cs | 2 +- .../Portable/Binder/ForEachLoopBinder.cs | 10 +- .../Binder/Semantics/BestTypeInferrer.cs | 34 +- .../Semantics/Conversions/Conversion.cs | 2 +- .../Binder/Semantics/INonNullTypesContext.cs | 6 + .../OverloadResolution/MethodTypeInference.cs | 108 ++-- .../OverloadResolution/OverloadResolution.cs | 4 +- .../BoundTree/BoundDiscardExpression.cs | 2 +- .../BoundTree/BoundExpressionExtensions.cs | 4 +- .../Portable/BoundTree/ConversionGroup.cs | 6 +- .../Portable/BoundTree/UnboundLambda.cs | 36 +- .../BoundTree/VariablePendingInference.cs | 8 +- .../DataFlowPass.LocalFunctions.cs | 2 +- .../Portable/FlowAnalysis/DataFlowPass.cs | 8 +- .../Portable/FlowAnalysis/DataFlowPassBase.cs | 4 +- .../Portable/FlowAnalysis/NullableWalker.cs | 158 ++--- .../LambdaRewriter/LambdaCapturedVariable.cs | 2 +- .../Lowering/MethodToClassRewriter.cs | 2 +- .../Lowering/SynthesizedMethodBaseSymbol.cs | 2 +- .../SymbolDisplayVisitor.Types.cs | 14 +- .../Portable/Symbols/AbstractTypeMap.cs | 6 +- .../AnonymousTypes/AnonymousTypeField.cs | 2 +- .../AnonymousTypeManager.Templates.cs | 2 +- .../Portable/Symbols/ArrayTypeSymbol.cs | 6 +- .../Symbols/Attributes/AttributeData.cs | 2 +- .../Symbols/MemberSignatureComparer.cs | 4 +- .../Metadata/PE/NullableTypeDecoder.cs | 2 +- .../Symbols/Metadata/PE/PEEventSymbol.cs | 4 +- .../Symbols/Metadata/PE/PEFieldSymbol.cs | 8 +- .../Symbols/Metadata/PE/PEParameterSymbol.cs | 2 +- .../Symbols/Metadata/PE/TupleTypeDecoder.cs | 4 +- .../CSharp/Portable/Symbols/MethodSymbol.cs | 2 +- .../Symbols/MethodSymbolExtensions.cs | 6 +- .../Portable/Symbols/NamedTypeSymbol.cs | 20 +- .../Portable/Symbols/PointerTypeSymbol.cs | 6 +- .../Retargeting/RetargetingMethodSymbol.cs | 2 +- .../Retargeting/RetargetingPropertySymbol.cs | 2 +- .../RetargetingSymbolTranslator.cs | 6 +- .../Source/GlobalExpressionVariable.cs | 20 +- .../Portable/Symbols/Source/LambdaSymbol.cs | 12 +- .../Symbols/Source/LocalFunctionSymbol.cs | 6 +- .../Source/SourceEventAccessorSymbol.cs | 8 +- .../Symbols/Source/SourceLocalSymbol.cs | 32 +- .../Symbols/Source/SourceMemberFieldSymbol.cs | 27 +- .../Symbols/Source/SourcePropertySymbol.cs | 23 +- .../Symbols/SubstitutedEventSymbol.cs | 8 +- .../Symbols/SubstitutedFieldSymbol.cs | 8 +- .../Symbols/SubstitutedMethodSymbol.cs | 12 +- .../Symbols/SubstitutedParameterSymbol.cs | 9 +- .../Symbols/SubstitutedPropertySymbol.cs | 8 +- .../Portable/Symbols/SymbolExtensions.cs | 2 +- .../Portable/Symbols/SymbolWithAnnotations.cs | 611 +++++++++++------- .../Synthesized/SynthesizedParameterSymbol.cs | 2 +- .../Synthesized/TypeSubstitutedLocalSymbol.cs | 2 +- .../Symbols/Tuples/TupleTypeSymbol.cs | 2 +- .../CSharp/Portable/Symbols/TypeSymbol.cs | 4 +- .../Portable/Symbols/TypeSymbolExtensions.cs | 14 +- .../Portable/Symbols/TypeUnification.cs | 10 +- .../Test/Emit/CodeGen/CodeGenTupleTest.cs | 2 +- .../Symbol/Symbols/Source/DelegateTests.cs | 6 +- .../Utilities/CSharp/UsesIsNullableVisitor.cs | 2 +- .../ExpressionCompiler/CompilationContext.cs | 3 +- 74 files changed, 797 insertions(+), 630 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.NamespaceOrTypeOrAliasSymbolWithAnnotations.cs b/src/Compilers/CSharp/Portable/Binder/Binder.NamespaceOrTypeOrAliasSymbolWithAnnotations.cs index e7034128399d9..725edb4a5c734 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.NamespaceOrTypeOrAliasSymbolWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.NamespaceOrTypeOrAliasSymbolWithAnnotations.cs @@ -9,21 +9,23 @@ internal partial class Binder { internal struct NamespaceOrTypeOrAliasSymbolWithAnnotations { - private readonly object _symbolOrTypeSymbolWithAnnotations; + private readonly TypeSymbolWithAnnotations _type; + private readonly Symbol _symbol; - private NamespaceOrTypeOrAliasSymbolWithAnnotations(object symbolOrTypeSymbolWithAnnotations) + private NamespaceOrTypeOrAliasSymbolWithAnnotations(TypeSymbolWithAnnotations type, Symbol symbol) { - Debug.Assert(symbolOrTypeSymbolWithAnnotations != null); - Debug.Assert(!(symbolOrTypeSymbolWithAnnotations is TypeSymbol)); - _symbolOrTypeSymbolWithAnnotations = symbolOrTypeSymbolWithAnnotations; + Debug.Assert(type.IsNull != (symbol is null)); + Debug.Assert(!(symbol is TypeSymbol)); + _type = type; + _symbol = symbol; } - internal TypeSymbolWithAnnotations Type => _symbolOrTypeSymbolWithAnnotations as TypeSymbolWithAnnotations; - internal Symbol Symbol => _symbolOrTypeSymbolWithAnnotations as Symbol ?? Type?.TypeSymbol; - internal bool IsType => !(Type is null); - internal bool IsAlias => (_symbolOrTypeSymbolWithAnnotations as Symbol)?.Kind == SymbolKind.Alias; + internal TypeSymbolWithAnnotations Type => _type; + internal Symbol Symbol => _symbol ?? Type.TypeSymbol; + internal bool IsType => !_type.IsNull; + internal bool IsAlias => _symbol?.Kind == SymbolKind.Alias; internal NamespaceOrTypeSymbol NamespaceOrTypeSymbol => Symbol as NamespaceOrTypeSymbol; - internal bool IsDefault => _symbolOrTypeSymbolWithAnnotations is null; + internal bool IsDefault => _type.IsNull && _symbol is null; internal static NamespaceOrTypeOrAliasSymbolWithAnnotations CreateUnannotated(INonNullTypesContext nonNullTypesContext, Symbol symbol) { @@ -32,16 +34,14 @@ internal static NamespaceOrTypeOrAliasSymbolWithAnnotations CreateUnannotated(IN return default; } var type = symbol as TypeSymbol; - if (type is null) - { - return new NamespaceOrTypeOrAliasSymbolWithAnnotations(symbol); - } - return new NamespaceOrTypeOrAliasSymbolWithAnnotations(TypeSymbolWithAnnotations.CreateUnannotated(nonNullTypesContext, type)); + return type is null ? + new NamespaceOrTypeOrAliasSymbolWithAnnotations(default, symbol) : + new NamespaceOrTypeOrAliasSymbolWithAnnotations(TypeSymbolWithAnnotations.CreateUnannotated(nonNullTypesContext, type), null); } public static implicit operator NamespaceOrTypeOrAliasSymbolWithAnnotations(TypeSymbolWithAnnotations type) { - return new NamespaceOrTypeOrAliasSymbolWithAnnotations(type); + return new NamespaceOrTypeOrAliasSymbolWithAnnotations(type, null); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index e25e5ae00f728..bf255beb1643d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -916,7 +916,7 @@ private bool CheckPropertyValueKind(SyntaxNode node, BoundExpression expr, BindV // change from Dev10 which reports this error for struct types only, // not for type parameters constrained to "struct". - Debug.Assert((object)propertySymbol.Type != null); + Debug.Assert(!propertySymbol.Type.IsNull); Error(diagnostics, ErrorCode.ERR_ReturnNotLValue, expr.Syntax, propertySymbol); } else @@ -1622,7 +1622,7 @@ private static void ReportReadOnlyFieldError(FieldSymbol field, SyntaxNode node, { Debug.Assert((object)field != null); Debug.Assert(RequiresAssignableVariable(kind)); - Debug.Assert((object)field.Type != null); + Debug.Assert(!field.Type.IsNull); // It's clearer to say that the address can't be taken than to say that the field can't be modified // (even though the latter message gives more explanation of why). diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index 2ca6ec2293c99..09b5d1bc16c2a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -239,7 +239,7 @@ private void ValidateTypeForAttributeParameters(ImmutableArray foreach (var parameter in parameters) { var paramType = parameter.Type; - Debug.Assert((object)paramType != null); + Debug.Assert(!paramType.IsNull); if (!paramType.TypeSymbol.IsValidAttributeParameterType(Compilation)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index 43e6e86e97e8e..43972efe5f121 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs @@ -740,7 +740,7 @@ private static ImmutableArray PerformCrefOverloadResolution(ArrayBuilder containingType: null, name: null, refKind: RefKind.None, - returnType: null, + returnType: default, refCustomModifiers: ImmutableArray.Empty, explicitInterfaceImplementations: ImmutableArray.Empty); break; @@ -755,7 +755,7 @@ private static ImmutableArray PerformCrefOverloadResolution(ArrayBuilder containingType: null, name: null, refKind: RefKind.None, - type: null, + type: default, refCustomModifiers: ImmutableArray.Empty, isStatic: false, explicitInterfaceImplementations: ImmutableArray.Empty); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index 415f2da9765d8..f7e9480108c8b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -726,7 +726,7 @@ private DeconstructionVariable BindDeconstructionVariables( bool isConst = false; AliasSymbol alias; var declType = BindVariableType(component.Designation, diagnostics, component.Type, ref isConst, out isVar, out alias); - Debug.Assert(isVar == ((object)declType == null)); + Debug.Assert(isVar == declType.IsNull); if (component.Designation.Kind() == SyntaxKind.ParenthesizedVariableDesignation && !isVar) { // An explicit is not allowed with a parenthesized designation @@ -800,7 +800,7 @@ private static BoundDiscardExpression BindDiscardExpression( SyntaxNode syntax, TypeSymbolWithAnnotations declType) { - return new BoundDiscardExpression(syntax, declType?.TypeSymbol); + return new BoundDiscardExpression(syntax, declType.TypeSymbol); } /// @@ -824,7 +824,7 @@ private BoundExpression BindDeconstructionVariable( // might own nested scope. var hasErrors = localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics); - if ((object)declType != null) + if (!declType.IsNull) { return new BoundLocal(syntax, localSymbol, BoundLocalDeclarationKind.WithExplicitType, constantValueOpt: null, isNullableUnknown: false, type: declType.TypeSymbol, hasErrors: hasErrors); } @@ -844,7 +844,7 @@ private BoundExpression BindDeconstructionVariable( BoundThisReference receiver = ThisReference(designation, this.ContainingType, hasErrors: false, wasCompilerGenerated: true); - if ((object)declType != null) + if (!declType.IsNull) { var fieldType = field.GetFieldType(this.FieldsBeingBound); Debug.Assert(declType.TypeSymbol == fieldType.TypeSymbol); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 27745ce2d1827..f49bfda43f095 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -676,7 +676,7 @@ private BoundExpression BindDeclarationExpression(DeclarationExpressionSyntax no /// private BoundExpression BindDeclarationVariables(TypeSymbolWithAnnotations declType, VariableDesignationSyntax node, CSharpSyntaxNode syntax, DiagnosticBag diagnostics) { - declType = declType ?? TypeSymbolWithAnnotations.Create(CreateErrorType("var")); + declType = declType.IsNull ? TypeSymbolWithAnnotations.Create(CreateErrorType("var")) : declType; switch (node.Kind()) { case SyntaxKind.SingleVariableDesignation: @@ -783,7 +783,7 @@ private BoundExpression BindTupleExpression(TupleExpressionSyntax node, Diagnost var elementType = boundArgument.GetTypeAndNullability(includeNullability); elementTypes.Add(elementType); - if ((object)elementType == null) + if (elementType.IsNull) { hasNaturalType = false; } @@ -2056,7 +2056,7 @@ private void GenerateExplicitConversionErrorsForTupleLiteralArguments( /// private BoundExpression BindExplicitNullableCastFromNonNullable(ExpressionSyntax node, BoundExpression operand, TypeSymbolWithAnnotations targetType, DiagnosticBag diagnostics) { - Debug.Assert((object)targetType != null && targetType.IsNullableType()); + Debug.Assert(!targetType.IsNull && targetType.IsNullableType()); Debug.Assert(operand.Type != null && !operand.Type.IsNullableType()); // Section 6.2.3 of the spec only applies when the non-null version of the types involved have a @@ -2310,9 +2310,9 @@ private BoundExpression BindOutDeclarationArgument(DeclarationExpressionSyntax d bool isConst = false; AliasSymbol alias; var declType = BindVariableType(designation, diagnostics, typeSyntax, ref isConst, out isVar, out alias); - Debug.Assert(isVar == ((object)declType == null)); + Debug.Assert(isVar == declType.IsNull); - return new BoundDiscardExpression(declarationExpression, declType?.TypeSymbol); + return new BoundDiscardExpression(declarationExpression, declType.TypeSymbol); } case SyntaxKind.SingleVariableDesignation: return BindOutVariableDeclarationArgument(declarationExpression, diagnostics); @@ -2563,7 +2563,7 @@ private void CoerceArguments( else if (argument.Kind == BoundKind.DiscardExpression && !argument.HasExpressionType()) { TypeSymbolWithAnnotations parameterType = GetCorrespondingParameterType(ref result, parameters, arg); - Debug.Assert((object)parameterType != null); + Debug.Assert(!parameterType.IsNull); arguments[arg] = ((BoundDiscardExpression)argument).SetInferredType(parameterType); } } @@ -2711,7 +2711,7 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); - if ((object)bestType == null || bestType.SpecialType == SpecialType.System_Void) // Dev10 also reports ERR_ImplicitlyTypedArrayNoBestType for void. + if (bestType.IsNull || bestType.SpecialType == SpecialType.System_Void) // Dev10 also reports ERR_ImplicitlyTypedArrayNoBestType for void. { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, node); bestType = TypeSymbolWithAnnotations.Create(CreateErrorType()); @@ -2737,7 +2737,7 @@ private BoundExpression BindImplicitStackAllocArrayCreationExpression(ImplicitSt TypeSymbolWithAnnotations bestType = BestTypeInferrer.InferBestType(boundInitializerExpressions, this.Conversions, out bool hadMultipleCandidates, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); - if ((object)bestType == null || bestType.SpecialType == SpecialType.System_Void) + if (bestType.IsNull || bestType.SpecialType == SpecialType.System_Void) { Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, node); bestType = TypeSymbolWithAnnotations.Create(CreateErrorType()); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index af95bddeb89e9..d4a2f8b209683 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -113,7 +113,7 @@ private Tuple, ImmutableArray } var typeSyntax = p.Type; - TypeSymbolWithAnnotations type = null; + TypeSymbolWithAnnotations type = default; var refKind = RefKind.None; if (typeSyntax == null) @@ -228,7 +228,7 @@ private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticB foreach (var type in types) { // UNDONE: Where do we report improper use of pointer types? - if ((object)type != null && type.IsStatic) + if (!type.IsNull && type.IsStatic) { Error(diagnostics, ErrorCode.ERR_ParameterIsStaticClass, syntax, type.TypeSymbol); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 114534c7bc5f9..6e554dc3644ee 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -3645,7 +3645,7 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node useSiteDiagnostics: ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); - if ((object)bestType == null) + if (bestType.IsNull) { // CONSIDER: Dev10 suppresses ERR_InvalidQM unless the following is true for both trueType and falseType // (!T->type->IsErrorType() || T->type->AsErrorType()->HasTypeParent() || T->type->AsErrorType()->HasNSParent()) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 9c7cdc1bac391..7dd94310dbb06 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -253,7 +253,7 @@ private BoundPattern BindDeclarationPattern( declType = TypeSymbolWithAnnotations.CreateUnannotated(NonNullTypesContext, operandType); } - if (declType == (object)null) + if (declType.IsNull) { Debug.Assert(hasErrors); declType = TypeSymbolWithAnnotations.CreateUnannotated(NonNullTypesContext, this.CreateErrorType("var")); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index c1807d3b0a377..9259f405922af 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -664,7 +664,7 @@ declarationNode is VariableDesignationSyntax || // we want to treat the declaration as an explicitly typed declaration. TypeSymbolWithAnnotations declType = BindTypeOrVarKeyword(typeSyntax.SkipRef(out _), diagnostics, out isVar, out alias); - Debug.Assert((object)declType != null || isVar); + Debug.Assert(!declType.IsNull || isVar); if (isVar) { @@ -851,7 +851,7 @@ protected BoundLocalDeclaration BindVariableDeclaration( CSharpSyntaxNode associatedSyntaxNode = null) { Debug.Assert(declarator != null); - Debug.Assert((object)declTypeOpt != null || isVar); + Debug.Assert(!declTypeOpt.IsNull || isVar); Debug.Assert(typeSyntax != null); var localDiagnostics = DiagnosticBag.GetInstance(); @@ -934,7 +934,7 @@ protected BoundLocalDeclaration BindVariableDeclaration( } } - Debug.Assert((object)declTypeOpt != null); + Debug.Assert(!declTypeOpt.IsNull); if (kind == LocalDeclarationKind.FixedVariable) { @@ -2336,7 +2336,7 @@ internal BoundStatement BindForOrUsingOrFixedDeclarations(VariableDeclarationSyn bool isVar; TypeSymbolWithAnnotations declType = BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar, out alias); - Debug.Assert((object)declType != null || isVar); + Debug.Assert(!declType.IsNull || isVar); var variables = nodeOpt.Variables; int count = variables.Count; @@ -2475,7 +2475,7 @@ protected virtual TypeSymbol GetCurrentReturnType(out RefKind refKind) TypeSymbolWithAnnotations returnType = symbol.ReturnType; - if ((object)returnType == null || (object)returnType == LambdaSymbol.ReturnTypeIsBeingInferred) + if (returnType.IsNull || (object)returnType.TypeSymbol == LambdaSymbol.ReturnTypeIsBeingInferred) { return null; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 60dc985d4f69a..d4e0a02129e35 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -32,7 +32,7 @@ internal TypeSymbolWithAnnotations BindTypeOrVarKeyword(TypeSyntax syntax, Diagn { var symbol = BindTypeOrAliasOrVarKeyword(syntax, diagnostics, out isVar); Debug.Assert(isVar == symbol.IsDefault); - return isVar ? null : UnwrapAlias(symbol, diagnostics, syntax).Type; + return isVar ? default : UnwrapAlias(symbol, diagnostics, syntax).Type; } /// @@ -52,7 +52,7 @@ internal TypeSymbolWithAnnotations BindTypeOrUnmanagedKeyword(TypeSyntax syntax, { var symbol = BindTypeOrAliasOrUnmanagedKeyword(syntax, diagnostics, out isUnmanaged); Debug.Assert(isUnmanaged == symbol.IsDefault); - return isUnmanaged ? null : UnwrapAlias(symbol, diagnostics, syntax).Type; + return isUnmanaged ? default : UnwrapAlias(symbol, diagnostics, syntax).Type; } /// @@ -76,7 +76,7 @@ internal TypeSymbolWithAnnotations BindTypeOrVarKeyword(TypeSyntax syntax, Diagn if (isVar) { alias = null; - return null; + return default; } else { @@ -327,7 +327,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeSymbol(E internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeSymbol(ExpressionSyntax syntax, DiagnosticBag diagnostics, ConsList basesBeingResolved, bool suppressUseSiteDiagnostics) { var result = BindNamespaceOrTypeOrAliasSymbol(syntax, diagnostics, basesBeingResolved, suppressUseSiteDiagnostics); - Debug.Assert((object)result != null); + Debug.Assert(!result.IsDefault); return UnwrapAlias(result, diagnostics, syntax, basesBeingResolved); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs index 5e788df4a8e94..0329ab515c96d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs @@ -386,7 +386,7 @@ private static (ImmutableArray Elements, ImmutableArray // placeholder bound nodes with the proper types are sufficient to bind the element-wise binary operators TypeSymbol tupleType = expr.Type.StrippedType(); ImmutableArray placeholders = tupleType.TupleElementTypes - .SelectAsArray((t, s) => (BoundExpression)new BoundTupleOperandPlaceholder(s, t?.TypeSymbol), expr.Syntax); + .SelectAsArray((t, s) => (BoundExpression)new BoundTupleOperandPlaceholder(s, t.TypeSymbol), expr.Syntax); return (placeholders, tupleType.TupleElementNames); } diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachEnumeratorInfo.cs b/src/Compilers/CSharp/Portable/Binder/ForEachEnumeratorInfo.cs index acc1399276ab1..518f5dc6e949d 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachEnumeratorInfo.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachEnumeratorInfo.cs @@ -48,7 +48,7 @@ private ForEachEnumeratorInfo( BinderFlags location) { Debug.Assert((object)collectionType != null, "Field 'collectionType' cannot be null"); - Debug.Assert((object)elementType != null, "Field 'elementType' cannot be null"); + Debug.Assert(!elementType.IsNull, "Field 'elementType' cannot be null"); Debug.Assert((object)getEnumeratorMethod != null, "Field 'getEnumeratorMethod' cannot be null"); Debug.Assert((object)currentPropertyGetter != null, "Field 'currentPropertyGetter' cannot be null"); Debug.Assert((object)moveNextMethod != null, "Field 'moveNextMethod' cannot be null"); diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 76d31caaf2d08..618022d5f9c59 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -170,7 +170,7 @@ internal override BoundStatement BindForEachDeconstruction(DiagnosticBag diagnos ExpressionSyntax variables = ((ForEachVariableStatementSyntax)_syntax).Variable; // Tracking narrowest safe-to-escape scope by default, the proper val escape will be set when doing full binding of the foreach statement - var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, this.LocalScopeDepth, inferredType?.TypeSymbol ?? CreateErrorType("var")); + var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, this.LocalScopeDepth, inferredType.TypeSymbol ?? CreateErrorType("var")); DeclarationExpressionSyntax declaration = null; ExpressionSyntax expression = null; @@ -226,11 +226,11 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, if (isVar) { - declType = inferredType ?? TypeSymbolWithAnnotations.Create(CreateErrorType("var"), isNullableIfReferenceType: null); + declType = inferredType.IsNull ? TypeSymbolWithAnnotations.Create(CreateErrorType("var"), isNullableIfReferenceType: null) : inferredType; } else { - Debug.Assert((object)declType != null); + Debug.Assert(!declType.IsNull); } iterationVariableType = declType.TypeSymbol; @@ -289,7 +289,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, case SyntaxKind.ForEachVariableStatement: { var node = (ForEachVariableStatementSyntax)_syntax; - iterationVariableType = inferredType?.TypeSymbol ?? CreateErrorType("var"); + iterationVariableType = inferredType.TypeSymbol ?? CreateErrorType("var"); var variables = node.Variable; if (variables.IsDeconstructionLeft()) @@ -492,7 +492,7 @@ private bool GetEnumeratorInfoAndInferCollectionElementType(ref ForEachEnumerato if (!gotInfo) { - inferredType = null; + inferredType = default; } else if (collectionExpr.HasDynamicType()) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs index 6fe145add0f87..a136b9e0c53b5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs @@ -19,7 +19,7 @@ public static TypeSymbolWithAnnotations InferBestType(ImmutableArray false; } + + internal sealed class NonNullTypesUnusedContext : INonNullTypesContext + { + public static readonly INonNullTypesContext Instance = new NonNullTypesUnusedContext(); + public bool? NonNullTypes => throw ExceptionUtilities.Unreachable; + } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 10ba03e27fb43..fde80b0006d4e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -332,7 +332,7 @@ internal string Dump() var fixedType = _fixedResults[i]; - if ((object)fixedType == null) + if (fixedType.IsNull) { sb.Append("UNFIXED "); } @@ -378,7 +378,7 @@ private ImmutableArray GetResults() for (int i = 0; i < _methodTypeParameters.Length; i++) { - if ((object)_fixedResults[i] != null) + if (!_fixedResults[i].IsNull) { if (!_fixedResults[i].IsErrorType()) { @@ -405,12 +405,12 @@ private bool ValidIndex(int index) private bool IsUnfixed(int methodTypeParameterIndex) { Debug.Assert(ValidIndex(methodTypeParameterIndex)); - return (object)_fixedResults[methodTypeParameterIndex] == null; + return _fixedResults[methodTypeParameterIndex].IsNull; } private bool IsUnfixedTypeParameter(TypeSymbolWithAnnotations type) { - Debug.Assert((object)type != null); + Debug.Assert(!type.IsNull); if (type.TypeKind != TypeKind.TypeParameter) return false; @@ -1217,7 +1217,7 @@ private bool AnyDependsOn(int iParam) private void OutputTypeInference(Binder binder, BoundExpression expression, bool? isNullable, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { Debug.Assert(expression != null); - Debug.Assert((object)target != null); + Debug.Assert(!target.IsNull); // SPEC: An output type inference is made from an expression E to a type T // SPEC: in the following way: @@ -1240,7 +1240,7 @@ private void OutputTypeInference(Binder binder, BoundExpression expression, bool // SPEC: * Otherwise, if E is an expression with type U then a lower-bound // SPEC: inference is made from U to T. var sourceType = TypeSymbolWithAnnotations.Create(expression.Type, isNullable); - if ((object)sourceType != null) + if (!sourceType.IsNull) { LowerBoundInference(sourceType, target, ref useSiteDiagnostics); } @@ -1250,7 +1250,7 @@ private void OutputTypeInference(Binder binder, BoundExpression expression, bool private bool InferredReturnTypeInference(BoundExpression source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { Debug.Assert(source != null); - Debug.Assert((object)target != null); + Debug.Assert(!target.IsNull); // SPEC: * If E is an anonymous function with inferred return type U and // SPEC: T is a delegate type or expression tree with return type Tb // SPEC: then a lower bound inference is made from U to Tb. @@ -1266,13 +1266,13 @@ private bool InferredReturnTypeInference(BoundExpression source, TypeSymbolWithA Debug.Assert((object)delegateType.DelegateInvokeMethod != null && !delegateType.DelegateInvokeMethod.HasUseSiteError, "This method should only be called for valid delegate types."); var returnType = delegateType.DelegateInvokeMethod.ReturnType; - if ((object)returnType == null || returnType.SpecialType == SpecialType.System_Void) + if (returnType.IsNull || returnType.SpecialType == SpecialType.System_Void) { return false; } var inferredReturnType = InferReturnType(source, delegateType, ref useSiteDiagnostics); - if ((object)inferredReturnType == null) + if (inferredReturnType.IsNull) { return false; } @@ -1309,7 +1309,7 @@ private bool MethodGroupReturnTypeInference(Binder binder, BoundExpression sourc "This method should only be called for valid delegate types"); TypeSymbolWithAnnotations delegateReturnType = delegateInvokeMethod.ReturnType; - if ((object)delegateReturnType == null || delegateReturnType.SpecialType == SpecialType.System_Void) + if (delegateReturnType.IsNull || delegateReturnType.SpecialType == SpecialType.System_Void) { return false; } @@ -1372,7 +1372,7 @@ private static TypeSymbol MethodGroupReturnType( private void ExplicitParameterTypeInference(BoundExpression source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { Debug.Assert(source != null); - Debug.Assert((object)target != null); + Debug.Assert(!target.IsNull); // SPEC: An explicit type parameter type inference is made from an expression // SPEC: E to a type T in the following way. @@ -1430,8 +1430,8 @@ private void ExplicitParameterTypeInference(BoundExpression source, TypeSymbolWi // private void ExactInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: An exact inference from a type U to a type V is made as follows: @@ -1482,8 +1482,8 @@ private void ExactInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnno private bool ExactTypeParameterInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: * If V is one of the unfixed Xi then U is added to the set of bounds // SPEC: for Xi. @@ -1497,8 +1497,8 @@ private bool ExactTypeParameterInference(TypeSymbolWithAnnotations source, TypeS private bool ExactArrayInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: * Otherwise, if U is an array type UE[...] and V is an array type VE[...] // SPEC: of the same rank then an exact inference from UE to VE is made. @@ -1543,8 +1543,8 @@ private void ExactOrBoundsInference(ExactOrBoundsKind kind, TypeSymbolWithAnnota private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); if (source.IsNullableType() && target.IsNullableType()) { @@ -1568,8 +1568,8 @@ private bool ExactNullableInference(TypeSymbolWithAnnotations source, TypeSymbol private bool ExactOrBoundsTupleInference(ExactOrBoundsKind kind, TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // NOTE: we are losing tuple element names when unwrapping tuple types to underlying types. // that is ok, because we are inferring type parameters used in the matching elements, @@ -1600,8 +1600,8 @@ private bool ExactTupleInference(TypeSymbolWithAnnotations source, TypeSymbolWit private bool ExactConstructedInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: * Otherwise, if V is a constructed type C and U is a constructed // SPEC: type C then an exact inference @@ -1668,8 +1668,8 @@ private void ExactTypeArgumentInference(NamedTypeSymbol source, NamedTypeSymbol // private void LowerBoundInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: A lower-bound inference from a type U to a type V is made as follows: @@ -1750,8 +1750,8 @@ private void LowerBoundInference(TypeSymbolWithAnnotations source, TypeSymbolWit private bool LowerBoundTypeParameterInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: * If V is one of the unfixed Xi then U is added to the set of bounds // SPEC: for Xi. @@ -1774,7 +1774,7 @@ private static TypeSymbolWithAnnotations GetMatchingElementType(ArrayTypeSymbol var arrayTarget = (ArrayTypeSymbol)target; if (!arrayTarget.HasSameShapeAs(source)) { - return null; + return default; } return arrayTarget.ElementType; } @@ -1783,7 +1783,7 @@ private static TypeSymbolWithAnnotations GetMatchingElementType(ArrayTypeSymbol if (!source.IsSZArray) { - return null; + return default; } // Arrays are specified as being convertible to IEnumerable, ICollection and @@ -1792,7 +1792,7 @@ private static TypeSymbolWithAnnotations GetMatchingElementType(ArrayTypeSymbol if (!target.IsPossibleArrayGenericInterface()) { - return null; + return default; } return ((NamedTypeSymbol)target).TypeArgumentWithDefinitionUseSiteDiagnostics(0, ref useSiteDiagnostics); @@ -1819,7 +1819,7 @@ private bool LowerBoundArrayInference(TypeSymbol source, TypeSymbol target, ref var arraySource = (ArrayTypeSymbol)source; var elementSource = arraySource.ElementType; var elementTarget = GetMatchingElementType(arraySource, target, ref useSiteDiagnostics); - if ((object)elementTarget == null) + if (elementTarget.IsNull) { return false; } @@ -2062,8 +2062,8 @@ private void LowerBoundTypeArgumentInference(NamedTypeSymbol source, NamedTypeSy // private void UpperBoundInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: An upper-bound inference from a type U to a type V is made as follows: @@ -2118,8 +2118,8 @@ private void UpperBoundInference(TypeSymbolWithAnnotations source, TypeSymbolWit private bool UpperBoundTypeParameterInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: * If V is one of the unfixed Xi then U is added to the set of upper bounds // SPEC: for Xi. if (IsUnfixedTypeParameter(target)) @@ -2132,8 +2132,8 @@ private bool UpperBoundTypeParameterInference(TypeSymbolWithAnnotations source, private bool UpperBoundArrayInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)source != null); - Debug.Assert((object)target != null); + Debug.Assert(!source.IsNull); + Debug.Assert(!target.IsNull); // SPEC: * Otherwise, if V is an array type Ve[...] and U is an array // SPEC: type Ue[...] of the same rank, or if V is a one-dimensional array @@ -2150,7 +2150,7 @@ private bool UpperBoundArrayInference(TypeSymbolWithAnnotations source, TypeSymb var arrayTarget = (ArrayTypeSymbol)target.TypeSymbol; var elementTarget = arrayTarget.ElementType; var elementSource = GetMatchingElementType(arrayTarget, source.TypeSymbol, ref useSiteDiagnostics); - if ((object)elementSource == null) + if (elementSource.IsNull) { return false; } @@ -2174,8 +2174,8 @@ private bool UpperBoundNullableInference(TypeSymbolWithAnnotations source, TypeS private bool UpperBoundConstructedInference(TypeSymbolWithAnnotations sourceWithAnnotations, TypeSymbolWithAnnotations targetWithAnnotations, ref HashSet useSiteDiagnostics) { - Debug.Assert((object)sourceWithAnnotations != null); - Debug.Assert((object)targetWithAnnotations != null); + Debug.Assert(!sourceWithAnnotations.IsNull); + Debug.Assert(!targetWithAnnotations.IsNull); var source = sourceWithAnnotations.TypeSymbol.TupleUnderlyingTypeOrSelf(); var target = targetWithAnnotations.TypeSymbol.TupleUnderlyingTypeOrSelf(); @@ -2371,7 +2371,7 @@ private bool Fix(int iParam, ref HashSet useSiteDiagnostics) var withoutNullability = Fix(exact, lower, upper, ref useSiteDiagnostics, _conversions, includeNullability: false); var best = withNullability; - if ((object)best == null) + if (best.IsNull) { best = withoutNullability; } @@ -2391,7 +2391,7 @@ private bool Fix(int iParam, ref HashSet useSiteDiagnostics) } } - if ((object)best == null) + if (best.IsNull) { return false; } @@ -2442,13 +2442,13 @@ private static TypeSymbolWithAnnotations Fix( candidates.AddAll(exact, conversions); if (candidates.Count >= 2) { - return null; + return default; } } if (candidates.Count == 0) { - return null; + return default; } // Don't mutate the collection as we're iterating it. @@ -2510,7 +2510,7 @@ private static TypeSymbolWithAnnotations Fix( // SPEC: * If among the remaining candidate types there is a unique type V to // SPEC: which there is an implicit conversion from all the other candidate // SPEC: types, then the parameter is fixed to V. - TypeSymbolWithAnnotations best = null; + TypeSymbolWithAnnotations best = default; foreach (var candidate in initialCandidates) { foreach (var candidate2 in initialCandidates) @@ -2522,7 +2522,7 @@ private static TypeSymbolWithAnnotations Fix( } } - if ((object)best == null) + if (best.IsNull) { best = candidate; } @@ -2542,7 +2542,7 @@ private static TypeSymbolWithAnnotations Fix( else { // best candidate is not unique - best = null; + best = default; break; } @@ -2706,7 +2706,7 @@ private TypeSymbolWithAnnotations InferReturnType(BoundExpression source, NamedT if (source.Kind != BoundKind.UnboundLambda) { - return null; + return default; } var anonymousFunction = (UnboundLambda)source; @@ -2723,12 +2723,12 @@ private TypeSymbolWithAnnotations InferReturnType(BoundExpression source, NamedT var originalDelegateParameters = target.DelegateParameters(); if (originalDelegateParameters.IsDefault) { - return null; + return default; } if (originalDelegateParameters.Length != anonymousFunction.ParameterCount) { - return null; + return default; } } @@ -2745,12 +2745,12 @@ private TypeSymbolWithAnnotations InferReturnType(BoundExpression source, NamedT { if (!anonymousFunction.ParameterType(p).TypeSymbol.Equals(fixedDelegateParameters[p].Type.TypeSymbol, TypeCompareKind.IgnoreDynamicAndTupleNames)) { - return null; + return default; } } } - // Future optimization: We could return null if the delegate has out or ref parameters + // Future optimization: We could return default if the delegate has out or ref parameters // and the anonymous function is an implicitly typed lambda. It will not be applicable. // We have an entirely fixed delegate parameter list, which is of the same arity as @@ -2781,7 +2781,7 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray GetInferredTypeArguments() var builder = ArrayBuilder.GetInstance(); foreach (var fixedResult in _fixedResults) { - builder.Add(fixedResult?.TypeSymbol); + builder.Add(fixedResult.TypeSymbol); } return builder.ToImmutableAndFree(); } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 33a49a3bbd399..b6ac63dcfe9a3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -2229,7 +2229,7 @@ private bool ExpressionMatchExactly(BoundExpression node, TypeSymbol t, ref Hash // - an inferred return type X exists for E in the context of the parameter list of D(§7.5.2.12), and an identity conversion exists from X to Y var x = lambda.GetInferredReturnType(ref useSiteDiagnostics); - if ((object)x != null && Conversions.HasIdentityConversion(x.TypeSymbol, y)) + if (!x.IsNull && Conversions.HasIdentityConversion(x.TypeSymbol, y)) { return true; } @@ -2654,7 +2654,7 @@ private bool CanDowngradeConversionFromLambdaToNeither(BetterResult currentResul } var x = lambda.InferReturnType(d1, ref useSiteDiagnostics); - if ((object)x == null) + if (x.IsNull) { return true; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardExpression.cs index 229f963525061..50e6826521503 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDiscardExpression.cs @@ -9,7 +9,7 @@ internal partial class BoundDiscardExpression { public BoundExpression SetInferredType(TypeSymbolWithAnnotations type) { - Debug.Assert((object)Type == null && (object)type != null); + Debug.Assert((object)Type == null && !type.IsNull); // PROTOTYPE(NullableReferenceTypes): we're dropping the annotation return this.Update(type.TypeSymbol); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs index 97a35dc9730da..ef518ab55e0f1 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs @@ -214,7 +214,7 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres var type = expr.Type; if ((object)type == null) { - return null; + return default; } // PROTOTYPE(NullableReferenceTypes): Could we track nullability always, // even in C#7, but only report warnings when the feature is enabled? @@ -250,7 +250,7 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres case BoundKind.Call: return ((BoundCall)expr).Method.ReturnType.IsNullable; case BoundKind.Conversion: - return ((BoundConversion)expr).ConversionGroupOpt?.ExplicitType?.IsNullable == true ? (bool?)true : null; + return ((BoundConversion)expr).ConversionGroupOpt?.ExplicitType.IsNullable == true ? (bool?)true : null; case BoundKind.BinaryOperator: return ((BoundBinaryOperator)expr).MethodOpt?.ReturnType.IsNullable; case BoundKind.NullCoalescingOperator: diff --git a/src/Compilers/CSharp/Portable/BoundTree/ConversionGroup.cs b/src/Compilers/CSharp/Portable/BoundTree/ConversionGroup.cs index 63b16d679f2df..aec1e38691576 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/ConversionGroup.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/ConversionGroup.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] internal sealed class ConversionGroup { - internal ConversionGroup(Conversion conversion, TypeSymbolWithAnnotations explicitType = null) + internal ConversionGroup(Conversion conversion, TypeSymbolWithAnnotations explicitType = default) { Conversion = conversion; ExplicitType = explicitType; @@ -22,7 +22,7 @@ internal ConversionGroup(Conversion conversion, TypeSymbolWithAnnotations explic /// /// True if the conversion is an explicit conversion. /// - internal bool IsExplicitConversion => (object)ExplicitType != null; + internal bool IsExplicitConversion => !ExplicitType.IsNull; /// /// The conversion (from Conversions.ClassifyConversionFromExpression for @@ -43,7 +43,7 @@ internal ConversionGroup(Conversion conversion, TypeSymbolWithAnnotations explic internal string GetDebuggerDisplay() { var str = $"#{_id} {Conversion}"; - if ((object)ExplicitType != null) + if (!ExplicitType.IsNull) { str += $" ({ExplicitType})"; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 832fe6897d8ed..72d1386000a4b 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -110,7 +110,7 @@ public TypeSymbolWithAnnotations GetInferredReturnType(ref HashSet /// Indicates the type of return statement with no expression. Used in InferReturnType. /// - internal static readonly TypeSymbolWithAnnotations NoReturnExpression = TypeSymbolWithAnnotations.Create(new UnsupportedMetadataTypeSymbol()); + internal static readonly TypeSymbol NoReturnExpression = new UnsupportedMetadataTypeSymbol(); /// /// Behavior of this function should be kept aligned with . @@ -126,7 +126,7 @@ internal static InferredLambdaReturnType InferReturnType(ArrayBuilder<(RefKind, { refKind = rk; } - if ((object)type == NoReturnExpression) + if ((object)type.TypeSymbol == NoReturnExpression) { hasReturnWithoutArgument = true; } @@ -152,7 +152,7 @@ private static TypeSymbolWithAnnotations CalculateReturnType( TypeSymbolWithAnnotations bestResultType; if (resultTypes.IsDefaultOrEmpty) { - bestResultType = null; + bestResultType = default; } else if (resultTypes.Length == 1) { @@ -196,11 +196,11 @@ private static TypeSymbolWithAnnotations CalculateReturnType( return TypeSymbolWithAnnotations.Create(resultType); } - if ((object)bestResultType == null || bestResultType.SpecialType == SpecialType.System_Void) + if (bestResultType.IsNull || bestResultType.SpecialType == SpecialType.System_Void) { // If the best type was 'void', ERR_CantReturnVoid is reported while binding the "return void" // statement(s). - return null; + return default; } // Some non-void best type T was found; use delegate InvokeMethod @@ -251,7 +251,7 @@ public override BoundNode VisitReturnStatement(BoundReturnStatement node) { var expression = node.ExpressionOpt; var type = (expression is null) ? - NoReturnExpression : + TypeSymbolWithAnnotations.CreateUnannotated(NonNullTypesUnusedContext.Instance, NoReturnExpression) : TypeSymbolWithAnnotations.Create(expression.Type?.SetUnknownNullabilityForReferenceTypes()); _builder.Add((node.RefKind, type)); return null; @@ -378,7 +378,7 @@ internal IEnumerable InferredReturnTypes() foreach (var lambda in _returnInferenceCache.Values) { var type = lambda.InferredReturnType.Type; - if ((object)type != null) + if (!type.IsNull) { any = true; yield return type.TypeSymbol; @@ -388,7 +388,7 @@ internal IEnumerable InferredReturnTypes() if (!any) { var type = BindForErrorRecovery().InferredReturnType.Type; - if ((object)type != null) + if (!type.IsNull) { yield return type.TypeSymbol; } @@ -405,7 +405,7 @@ private TypeSymbolWithAnnotations DelegateReturnType(MethodSymbol invokeMethod, if ((object)invokeMethod == null) { refKind = CodeAnalysis.RefKind.None; - return null; + return default; } refKind = invokeMethod.RefKind; return invokeMethod.ReturnType; @@ -448,7 +448,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) { lambdaSymbol = returnInferenceLambda.Symbol; var lambdaReturnType = lambdaSymbol.ReturnType; - if ((object)LambdaSymbol.InferenceFailureReturnType != lambdaReturnType && + if ((object)LambdaSymbol.InferenceFailureReturnType != lambdaReturnType.TypeSymbol && lambdaReturnType.Equals(returnType, TypeCompareKind.CompareNullableModifiersForReferenceTypes) && lambdaSymbol.RefKind == refKind) { lambdaBodyBinder = returnInferenceLambda.Binder; @@ -478,10 +478,10 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) var lambdaParameters = lambdaSymbol.Parameters; ParameterHelpers.EnsureIsReadOnlyAttributeExists(lambdaParameters, diagnostics, modifyCompilation: false); - if ((object)returnType != null) + if (!returnType.IsNull) { returnType.ReportAnnotatedUnconstrainedTypeParameterIfAny(lambdaSymbol.DiagnosticLocation, diagnostics); - if (returnType.ContainsNullableReferenceTypes() == true) + if (returnType.ContainsNullableReferenceTypes()) { binder.Compilation.EnsureNullableAttributeExists(diagnostics, lambdaSymbol.DiagnosticLocation, modifyCompilation: false); } @@ -513,7 +513,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) if (IsAsync && !ErrorFacts.PreventsSuccessfulDelegateConversion(diagnostics)) { - if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type. + if (!returnType.IsNull && // Can be null if "delegateType" is not actually a delegate type. returnType.SpecialType != SpecialType.System_Void && !returnType.TypeSymbol.IsNonGenericTaskType(binder.Compilation) && !returnType.TypeSymbol.IsGenericTaskType(binder.Compilation)) @@ -570,7 +570,7 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType, Immutabl parameterTypes, parameterRefKinds, refKind: CodeAnalysis.RefKind.None, - returnType: null, + returnType: default, diagnostics: diagnostics); Binder lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder)); var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); @@ -582,7 +582,11 @@ private BoundLambda ReallyInferReturnType(NamedTypeSymbol delegateType, Immutabl { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; // TODO: Should InferredReturnType.UseSiteDiagnostics be merged into BoundLambda.Diagnostics? - var returnType = inferredReturnType.Type ?? LambdaSymbol.InferenceFailureReturnType; + var returnType = inferredReturnType.Type; + if (returnType.IsNull) + { + returnType = TypeSymbolWithAnnotations.CreateUnannotated(NonNullTypesFalseContext.Instance, LambdaSymbol.InferenceFailureReturnType); + } lambdaSymbol.SetInferredReturnType(inferredReturnType.RefKind, returnType); return result; @@ -788,7 +792,7 @@ private static string GetLambdaSortString(LambdaSymbol lambda) builder.Builder.Append(parameter.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)); } - if ((object)lambda.ReturnType != null) + if (!lambda.ReturnType.IsNull) { builder.Builder.Append(lambda.ReturnType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs b/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs index 12526860d9e3d..138bab24a71af 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/VariablePendingInference.cs @@ -22,19 +22,19 @@ internal partial class VariablePendingInference : BoundExpression { internal BoundExpression SetInferredType(TypeSymbolWithAnnotations type, DiagnosticBag diagnosticsOpt) { - Debug.Assert((object)type != null); + Debug.Assert(!type.IsNull); return SetInferredType(type, null, diagnosticsOpt); } internal BoundExpression SetInferredType(TypeSymbolWithAnnotations type, Binder binderOpt, DiagnosticBag diagnosticsOpt) { - Debug.Assert(binderOpt != null || (object)type != null); + Debug.Assert(binderOpt != null || !type.IsNull); Debug.Assert(this.Syntax.Kind() == SyntaxKind.SingleVariableDesignation || (this.Syntax.Kind() == SyntaxKind.DeclarationExpression && ((DeclarationExpressionSyntax)this.Syntax).Designation.Kind() == SyntaxKind.SingleVariableDesignation)); - bool inferenceFailed = ((object)type == null); + bool inferenceFailed = type.IsNull; if (inferenceFailed) { @@ -93,7 +93,7 @@ internal BoundExpression SetInferredType(TypeSymbolWithAnnotations type, Binder internal BoundExpression FailInference(Binder binder, DiagnosticBag diagnosticsOpt) { - return this.SetInferredType(null, binder, diagnosticsOpt); + return this.SetInferredType(default, binder, diagnosticsOpt); } private void ReportInferenceFailure(DiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.LocalFunctions.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.LocalFunctions.cs index 22a03f329c249..57789b3dcc3a5 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.LocalFunctions.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.LocalFunctions.cs @@ -222,7 +222,7 @@ private void RecordReadInLocalFunction(int slot) // fields we need to record each field assignment separately, // since some fields may be assigned when this read is replayed VariableIdentifier id = variableBySlot[slot]; - var type = VariableType(id.Symbol)?.TypeSymbol; + var type = VariableType(id.Symbol).TypeSymbol; Debug.Assert(!_emptyStructTypeCache.IsEmptyStructType(type)); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index 0ef92c741d5b1..4e24163f81b73 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs @@ -883,7 +883,7 @@ protected virtual void ReportUnassigned(Symbol symbol, SyntaxNode node, int slot // We've already reported the use of a local before its declaration. No need to emit // another diagnostic for the same issue. } - else if (!_alreadyReported[slot] && VariableType(symbol)?.IsErrorType() != true) + else if (!_alreadyReported[slot] && !VariableType(symbol).IsErrorType()) { // CONSIDER: could suppress this diagnostic in cases where the local was declared in a using // or fixed statement because there's a special error code for not initializing those. @@ -1197,7 +1197,7 @@ private bool FieldsAllSet(int containingSlot, LocalState state) Debug.Assert(containingSlot != -1); Debug.Assert(!state.IsAssigned(containingSlot)); VariableIdentifier variable = variableBySlot[containingSlot]; - NamedTypeSymbol structType = (NamedTypeSymbol)VariableType(variable.Symbol)?.TypeSymbol; + NamedTypeSymbol structType = (NamedTypeSymbol)VariableType(variable.Symbol).TypeSymbol; foreach (var field in _emptyStructTypeCache.GetStructInstanceFields(structType)) { if (_emptyStructTypeCache.IsEmptyStructType(field.Type.TypeSymbol)) continue; @@ -1225,7 +1225,7 @@ private void SetSlotAssigned(int slot, ref LocalState state) { if (slot < 0) return; VariableIdentifier id = variableBySlot[slot]; - TypeSymbol type = VariableType(id.Symbol)?.TypeSymbol; + TypeSymbol type = VariableType(id.Symbol).TypeSymbol; Debug.Assert(!_emptyStructTypeCache.IsEmptyStructType(type)); if (slot >= state.Assigned.Capacity) Normalize(ref state); if (state.IsAssigned(slot)) return; // was already fully assigned. @@ -1261,7 +1261,7 @@ private void SetSlotUnassigned(int slot, ref LocalState state) { if (slot < 0) return; VariableIdentifier id = variableBySlot[slot]; - TypeSymbol type = VariableType(id.Symbol)?.TypeSymbol; + TypeSymbol type = VariableType(id.Symbol).TypeSymbol; Debug.Assert(!_emptyStructTypeCache.IsEmptyStructType(type)); if (!state.IsAssigned(slot)) return; // was already unassigned state.Unassign(slot); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPassBase.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPassBase.cs index c1e543565ef3d..0950ea6f48431 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPassBase.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPassBase.cs @@ -101,7 +101,7 @@ protected int GetOrCreateSlot(Symbol symbol, int containingSlot = 0) // Since analysis may proceed in multiple passes, it is possible the slot is already assigned. if (!_variableSlot.TryGetValue(identifier, out slot)) { - var variableType = VariableType(symbol)?.TypeSymbol; + var variableType = VariableType(symbol).TypeSymbol; if (_emptyStructTypeCache.IsEmptyStructType(variableType)) { return -1; @@ -239,7 +239,7 @@ protected static TypeSymbolWithAnnotations VariableType(Symbol s) return ((ParameterSymbol)s).Type; case SymbolKind.Method: Debug.Assert(((MethodSymbol)s).MethodKind == MethodKind.LocalFunction); - return null; + return default; case SymbolKind.Property: return ((PropertySymbol)s).Type; case SymbolKind.Event: diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 3b3e0bdf51173..ac888fbf0b182 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -488,7 +488,7 @@ private bool ReportNullReferenceAssignmentIfNecessary(BoundExpression value, Typ Debug.Assert(value != null); Debug.Assert(!IsConditionalState); - if (targetType is null || valueType is null) + if (targetType.IsNull || valueType.IsNull) { return false; } @@ -516,7 +516,7 @@ private void ReportAssignmentWarnings(BoundExpression value, TypeSymbolWithAnnot if (this.State.Reachable) { - if (targetType is null || valueType is null) + if (targetType.IsNull || valueType.IsNull) { return; } @@ -536,7 +536,7 @@ private void TrackNullableStateForAssignment(BoundExpression value, TypeSymbolWi if (this.State.Reachable) { - if ((object)targetType == null) + if (targetType.IsNull) { return; } @@ -553,7 +553,7 @@ private void TrackNullableStateForAssignment(BoundExpression value, TypeSymbolWi // Since reference can point to the heap, we cannot assume the value is not null after this assignment, // regardless of what value is being assigned. (targetType.IsNullable == true) ? (bool?)false : null : - !valueType?.IsNullable; + !valueType.IsNullable; // PROTOTYPE(NullableReferenceTypes): Might this clear state that // should be copied in InheritNullableStateOfTrackableType? @@ -571,7 +571,7 @@ private void TrackNullableStateForAssignment(BoundExpression value, TypeSymbolWi // } // For now, we copy a limited set of BoundNode types that shouldn't contain cycles. if ((value.Kind == BoundKind.ObjectCreationExpression || value.Kind == BoundKind.AnonymousObjectCreationExpression || value.Kind == BoundKind.DynamicObjectCreationExpression || targetType.TypeSymbol.IsAnonymousType) && - targetType.TypeSymbol.Equals(valueType?.TypeSymbol, TypeCompareKind.ConsiderEverything)) // PROTOTYPE(NullableReferenceTypes): Allow assignment to base type. + targetType.TypeSymbol.Equals(valueType.TypeSymbol, TypeCompareKind.ConsiderEverything)) // PROTOTYPE(NullableReferenceTypes): Allow assignment to base type. { if (valueSlot > 0) { @@ -580,7 +580,7 @@ private void TrackNullableStateForAssignment(BoundExpression value, TypeSymbolWi } } else if (EmptyStructTypeCache.IsTrackableStructType(targetType.TypeSymbol) && - targetType.TypeSymbol.Equals(valueType?.TypeSymbol, TypeCompareKind.ConsiderEverything)) + targetType.TypeSymbol.Equals(valueType.TypeSymbol, TypeCompareKind.ConsiderEverything)) { InheritNullableStateOfTrackableStruct(targetType.TypeSymbol, targetSlot, valueSlot, IsByRefTarget(targetSlot), slotWatermark: GetSlotWatermark()); } @@ -847,7 +847,7 @@ private void VisitPattern(BoundExpression expression, TypeSymbolWithAnnotations // PROTOTYPE(NullableReferenceTypes): We should only report such // diagnostics for locals that are set or checked explicitly within this method. - if (expressionResultType?.IsNullable == false && isNull == true) + if (!expressionResultType.IsNull && expressionResultType.IsNullable == false && isNull == true) { ReportStaticNullCheckingDiagnostics(ErrorCode.HDN_NullCheckIsProbablyAlwaysFalse, pattern.Syntax); } @@ -891,7 +891,7 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n TypeSymbolWithAnnotations resultType = ApplyConversion(expr, expr, conversion, returnType.TypeSymbol, result, checkConversion: true, fromExplicitCast: false, out bool canConvertNestedNullability); if (!canConvertNestedNullability) { - ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInAssignment, expr.Syntax, GetTypeAsDiagnosticArgument(result?.TypeSymbol), returnType.TypeSymbol); + ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInAssignment, expr.Syntax, GetTypeAsDiagnosticArgument(result.TypeSymbol), returnType.TypeSymbol); } bool returnTypeIsNonNullable = IsNonNullable(returnType); @@ -905,7 +905,7 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n if (!reportedNullable) { if (IsNullable(resultType) && (returnTypeIsNonNullable || returnTypeIsUnconstrainedTypeParameter) || - IsUnconstrainedTypeParameter(resultType?.TypeSymbol) && returnTypeIsNonNullable) + IsUnconstrainedTypeParameter(resultType.TypeSymbol) && returnTypeIsNonNullable) { ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullReferenceReturn, node.ExpressionOpt.Syntax); } @@ -918,7 +918,7 @@ private TypeSymbolWithAnnotations GetReturnType() { var method = (MethodSymbol)_member; var returnType = (_useMethodSignatureReturnType ? _methodSignatureOpt : method).ReturnType; - Debug.Assert((object)returnType != LambdaSymbol.ReturnTypeIsBeingInferred); + Debug.Assert((object)returnType.TypeSymbol != LambdaSymbol.ReturnTypeIsBeingInferred); return method.IsGenericTaskReturningAsync(compilation) ? ((NamedTypeSymbol)returnType.TypeSymbol).TypeArgumentsNoUseSiteDiagnostics.Single() : returnType; @@ -926,12 +926,12 @@ private TypeSymbolWithAnnotations GetReturnType() private static bool IsNullable(TypeSymbolWithAnnotations typeOpt) { - return typeOpt?.IsNullable == true; + return !typeOpt.IsNull && typeOpt.IsNullable == true; } private static bool IsNonNullable(TypeSymbolWithAnnotations typeOpt) { - return typeOpt?.IsNullable == false && typeOpt.IsReferenceType; + return !typeOpt.IsNull && typeOpt.IsNullable == false && typeOpt.IsReferenceType; } private static bool IsUnconstrainedTypeParameter(TypeSymbol typeOpt) @@ -980,7 +980,7 @@ public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node) if (node.DeclaredType.InferredType) { Debug.Assert(conversion.IsIdentity); - if (valueType is null) + if (valueType.IsNull) { Debug.Assert(type.IsErrorType()); valueType = type; @@ -996,7 +996,7 @@ public override BoundNode VisitLocalDeclaration(BoundLocalDeclaration node) ReportNullReferenceAssignmentIfNecessary(initializer, type, valueType, useLegacyWarnings: true); if (!canConvertNestedNullability) { - ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInAssignment, initializer.Syntax, GetTypeAsDiagnosticArgument(unconvertedType?.TypeSymbol), type.TypeSymbol); + ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInAssignment, initializer.Syntax, GetTypeAsDiagnosticArgument(unconvertedType.TypeSymbol), type.TypeSymbol); } } @@ -1012,8 +1012,8 @@ protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpress #if DEBUG // Verify Visit method set _result. TypeSymbolWithAnnotations resultType = _resultType; - Debug.Assert((object)resultType != _invalidType); - Debug.Assert(AreCloseEnough(resultType?.TypeSymbol, node.Type)); + Debug.Assert((object)resultType.TypeSymbol != _invalidType.TypeSymbol); + Debug.Assert(AreCloseEnough(resultType.TypeSymbol, node.Type)); #endif if (_callbackOpt != null) { @@ -1282,19 +1282,22 @@ private ArrayTypeSymbol VisitArrayInitializer(BoundArrayCreation node) // Should do the same here. HashSet useSiteDiagnostics = null; // If there are error types, use the first error type. (Matches InferBestType(ImmutableArray, ...).) - var bestType = resultTypes.FirstOrDefault(t => t?.IsErrorType() == true) ?? - BestTypeInferrer.InferBestType(resultTypes, _conversions, useSiteDiagnostics: ref useSiteDiagnostics); + var bestType = resultTypes.FirstOrDefault(t => !t.IsNull && t.IsErrorType()); + if (bestType.IsNull) + { + bestType = BestTypeInferrer.InferBestType(resultTypes, _conversions, useSiteDiagnostics: ref useSiteDiagnostics); + } // PROTOTYPE(NullableReferenceTypes): Report a special ErrorCode.WRN_NoBestNullabilityArrayElements // when InferBestType fails, and avoid reporting conversion warnings for each element in those cases. // (See similar code for conditional expressions: ErrorCode.WRN_NoBestNullabilityConditionalExpression.) - if ((object)bestType != null) + if (!bestType.IsNull) { elementType = bestType; } arrayType = arrayType.WithElementType(elementType); } - if ((object)elementType != null) + if (!elementType.IsNull) { bool elementTypeIsReferenceType = elementType.IsReferenceType == true; for (int i = 0; i < n; i++) @@ -1302,7 +1305,7 @@ private ArrayTypeSymbol VisitArrayInitializer(BoundArrayCreation node) var conversion = conversionBuilder[i]; var element = elementBuilder[i]; var resultType = resultBuilder[i]; - var sourceType = resultType?.TypeSymbol; + var sourceType = resultType.TypeSymbol; if (elementTypeIsReferenceType) { resultType = ApplyConversion(element, element, conversion, elementType.TypeSymbol, resultType, checkConversion: true, fromExplicitCast: false, out bool canConvertNestedNullability); @@ -1347,14 +1350,14 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node) Debug.Assert(node.Expression.Type.IsReferenceType); CheckPossibleNullReceiver(node.Expression, checkType: false); - var type = _resultType?.TypeSymbol as ArrayTypeSymbol; + var type = _resultType.TypeSymbol as ArrayTypeSymbol; foreach (var i in node.Indices) { VisitRvalue(i); } - _resultType = type?.ElementType; + _resultType = type?.ElementType ?? default; return null; } @@ -1385,8 +1388,8 @@ private TypeSymbolWithAnnotations InferResultNullability(BinaryOperatorKind oper { case BinaryOperatorKind.DelegateCombination: { - bool? leftIsNullable = leftType?.IsNullable; - bool? rightIsNullable = rightType?.IsNullable; + bool? leftIsNullable = leftType.IsNullable; + bool? rightIsNullable = rightType.IsNullable; if (leftIsNullable == false || rightIsNullable == false) { isNullable = false; @@ -1444,7 +1447,7 @@ protected override void AfterLeftChildHasBeenVisited(BoundBinaryOperator binary) if (op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) { BoundExpression operandComparedToNull = null; - TypeSymbolWithAnnotations operandComparedToNullType = null; + TypeSymbolWithAnnotations operandComparedToNullType = default; if (binary.Right.ConstantValue?.IsNull == true) { @@ -1462,7 +1465,7 @@ protected override void AfterLeftChildHasBeenVisited(BoundBinaryOperator binary) // PROTOTYPE(NullableReferenceTypes): This check is incorrect since it compares declared // nullability rather than tracked nullability. Moreover, we should only report such // diagnostics for locals that are set or checked explicitly within this method. - if (operandComparedToNullType?.IsNullable == false) + if (!operandComparedToNullType.IsNull && operandComparedToNullType.IsNullable == false) { ReportStaticNullCheckingDiagnostics(op == BinaryOperatorKind.Equal ? ErrorCode.HDN_NullCheckIsProbablyAlwaysFalse : @@ -1619,7 +1622,7 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato } var leftState = this.State.Clone(); - if (leftResult?.IsNullable == false) + if (leftResult.IsNullable == false) { ReportStaticNullCheckingDiagnostics(ErrorCode.HDN_ExpressionIsProbablyNeverNull, leftOperand.Syntax); } @@ -1635,8 +1638,8 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato rightResult = VisitRvalueWithResult(rightOperand); IntersectWith(ref this.State, ref leftState); TypeSymbol resultType; - var leftResultType = leftResult?.TypeSymbol; - var rightResultType = rightResult?.TypeSymbol; + var leftResultType = leftResult.TypeSymbol; + var rightResultType = rightResult.TypeSymbol; switch (node.OperatorResultKind) { case BoundNullCoalescingOperatorResultKind.NoCommonType: @@ -1665,7 +1668,7 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato _resultType = TypeSymbolWithAnnotations.Create(resultType, resultIsNullable); return null; - bool? getIsNullable(BoundExpression e, TypeSymbolWithAnnotations t) => (t is null) ? e.IsNullable() : t.IsNullable; + bool? getIsNullable(BoundExpression e, TypeSymbolWithAnnotations t) => t.IsNull ? e.IsNullable() : t.IsNullable; TypeSymbol getLeftResultType(TypeSymbol leftType, TypeSymbol rightType) { // If there was an identity conversion between the two operands (in short, if there @@ -1699,7 +1702,7 @@ public override BoundNode VisitConditionalAccess(BoundConditionalAccess node) if (receiver.Type?.IsReferenceType == true) { - if (receiverType?.IsNullable == false) + if (receiverType.IsNullable == false) { ReportStaticNullCheckingDiagnostics(ErrorCode.HDN_ExpressionIsProbablyNeverNull, receiver.Syntax); } @@ -1722,7 +1725,7 @@ public override BoundNode VisitConditionalAccess(BoundConditionalAccess node) // PROTOTYPE(NullableReferenceTypes): Use flow analysis type rather than node.Type // so that nested nullability is inferred from flow analysis. See VisitConditionalOperator. - _resultType = TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: receiverType?.IsNullable | _resultType?.IsNullable); + _resultType = TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: receiverType.IsNullable | _resultType.IsNullable); // PROTOTYPE(NullableReferenceTypes): Report conversion warnings. return null; } @@ -1766,7 +1769,7 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node TypeSymbolWithAnnotations resultType; if (node.HasErrors) { - resultType = null; + resultType = default; } else { @@ -1785,23 +1788,23 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node _conversions, out _, ref useSiteDiagnostics); - if (resultType is null) + if (resultType.IsNull) { ReportStaticNullCheckingDiagnostics( ErrorCode.WRN_NoBestNullabilityConditionalExpression, node.Syntax, - GetTypeAsDiagnosticArgument(consequenceResult?.TypeSymbol), - GetTypeAsDiagnosticArgument(alternativeResult?.TypeSymbol)); + GetTypeAsDiagnosticArgument(consequenceResult.TypeSymbol), + GetTypeAsDiagnosticArgument(alternativeResult.TypeSymbol)); } } - resultType = TypeSymbolWithAnnotations.Create(resultType?.TypeSymbol ?? node.Type.SetUnknownNullabilityForReferenceTypes(), isNullableIfReferenceType); + resultType = TypeSymbolWithAnnotations.Create(resultType.TypeSymbol ?? node.Type.SetUnknownNullabilityForReferenceTypes(), isNullableIfReferenceType); _resultType = resultType; return null; bool? getIsNullableIfReferenceType(BoundExpression expr, TypeSymbolWithAnnotations type) { - if ((object)type != null) + if (!type.IsNull) { return type.IsNullable; } @@ -1814,7 +1817,7 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node BoundExpression createPlaceholderIfNecessary(BoundExpression expr, TypeSymbolWithAnnotations type) { - return type is null ? + return type.IsNull ? expr : new BoundValuePlaceholder(expr.Syntax, type.IsNullable, type.TypeSymbol); } @@ -2268,7 +2271,7 @@ private void VisitArgumentConversion( TypeSymbolWithAnnotations parameterType, TypeSymbolWithAnnotations resultType) { - var argumentType = resultType?.TypeSymbol; + var argumentType = resultType.TypeSymbol; switch (refKind) { case RefKind.None: @@ -2395,7 +2398,7 @@ private static (ParameterSymbol Parameter, TypeSymbolWithAnnotations Type) GetCo { if (parameters.IsDefault) { - return (null, null); + return (default, default); } int n = parameters.Length; @@ -2434,7 +2437,7 @@ private static (ParameterSymbol Parameter, TypeSymbolWithAnnotations Type) GetCo if (parameter is null) { Debug.Assert(!expanded); - return (null, null); + return (default, default); } var type = parameter.Type; @@ -2516,7 +2519,7 @@ BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, Type // to re-bind lambdas in MethodTypeInferrer. return GetUnboundLambda((BoundLambda)argument, GetVariableState()); } - if (argumentType is null) + if (argumentType.IsNull) { return argument; } @@ -2649,7 +2652,7 @@ private TypeSymbolWithAnnotations GetAdjustedResult(TypeSymbolWithAnnotations ty private Symbol AsMemberOfResultType(Symbol symbol) { - var containingType = _resultType?.TypeSymbol as NamedTypeSymbol; + var containingType = _resultType.TypeSymbol as NamedTypeSymbol; if ((object)containingType == null || containingType.IsErrorType()) { return symbol; @@ -2735,20 +2738,20 @@ public override BoundNode VisitConversion(BoundConversion node) Visit(operand); TypeSymbolWithAnnotations operandType = _resultType; - TypeSymbolWithAnnotations explicitType = node.ConversionGroupOpt?.ExplicitType; - bool fromExplicitCast = (object)explicitType != null; - TypeSymbolWithAnnotations resultType = ApplyConversion(node, operand, conversion, explicitType?.TypeSymbol ?? node.Type, operandType, checkConversion: !fromExplicitCast, fromExplicitCast: fromExplicitCast, out bool _); + TypeSymbolWithAnnotations explicitType = node.ConversionGroupOpt?.ExplicitType ?? default; + bool fromExplicitCast = !explicitType.IsNull; + TypeSymbolWithAnnotations resultType = ApplyConversion(node, operand, conversion, explicitType.TypeSymbol ?? node.Type, operandType, checkConversion: !fromExplicitCast, fromExplicitCast: fromExplicitCast, out bool _); if (fromExplicitCast && explicitType.IsNullable == false) { TypeSymbol targetType = explicitType.TypeSymbol; bool reportNullable = false; - if (targetType.IsReferenceType && IsUnconstrainedTypeParameter(resultType?.TypeSymbol)) + if (targetType.IsReferenceType && IsUnconstrainedTypeParameter(resultType.TypeSymbol)) { reportNullable = true; } else if ((targetType.IsReferenceType || IsUnconstrainedTypeParameter(targetType)) && - resultType?.IsNullable == true) + resultType.IsNullable == true) { reportNullable = true; } @@ -2780,7 +2783,7 @@ private void VisitTupleExpression(BoundTupleExpression node) ImmutableArray elementTypes = arguments.SelectAsArray((a, w) => w.VisitRvalueWithResult(a), this); var tupleOpt = (TupleTypeSymbol)node.Type; _resultType = (tupleOpt is null) ? - null : + default : TypeSymbolWithAnnotations.Create(tupleOpt.WithElementTypes(elementTypes)); } @@ -2909,7 +2912,7 @@ private TypeSymbolWithAnnotations ApplyConversion( out bool canConvertNestedNullability) { Debug.Assert(node != null); - Debug.Assert(operandOpt != null || (object)operandType != null); + Debug.Assert(operandOpt != null || !operandType.IsNull); Debug.Assert((object)targetType != null); bool? isNullableIfReferenceType = null; @@ -2996,7 +2999,7 @@ private TypeSymbolWithAnnotations ApplyConversion( case ConversionKind.ExplicitDynamic: case ConversionKind.ImplicitDynamic: - isNullableIfReferenceType = operandType?.IsNullable; + isNullableIfReferenceType = operandType.IsNull ? null : operandType.IsNullable; break; case ConversionKind.Unboxing: @@ -3004,19 +3007,20 @@ private TypeSymbolWithAnnotations ApplyConversion( break; case ConversionKind.Boxing: - if (operandType?.IsValueType == true) + if (!operandType.IsNull && operandType.IsValueType) { // PROTOTYPE(NullableReferenceTypes): Should we worry about a pathological case of boxing nullable value known to be not null? // For example, new int?(0) isNullableIfReferenceType = operandType.IsNullableType(); } - else if (IsUnconstrainedTypeParameter(operandType?.TypeSymbol)) + else if (!operandType.IsNull && IsUnconstrainedTypeParameter(operandType.TypeSymbol)) { isNullableIfReferenceType = true; } else { - Debug.Assert(operandType?.IsReferenceType != true || + Debug.Assert(operandType.IsNull || + !operandType.IsReferenceType || operandType.SpecialType == SpecialType.System_ValueType || operandType.TypeKind == TypeKind.Interface || operandType.TypeKind == TypeKind.Dynamic); @@ -3031,7 +3035,7 @@ private TypeSymbolWithAnnotations ApplyConversion( case ConversionKind.Identity: case ConversionKind.ImplicitReference: case ConversionKind.ExplicitReference: - if (operandType is null && operandOpt.IsLiteralNullOrDefault()) + if (operandType.IsNull && operandOpt.IsLiteralNullOrDefault()) { isNullableIfReferenceType = true; } @@ -3041,10 +3045,10 @@ private TypeSymbolWithAnnotations ApplyConversion( if (checkConversion) { // PROTOTYPE(NullableReferenceTypes): Assert conversion is similar to original. - conversion = GenerateConversion(_conversions, operandOpt, operandType?.TypeSymbol, targetType, fromExplicitCast); + conversion = GenerateConversion(_conversions, operandOpt, operandType.TypeSymbol, targetType, fromExplicitCast); canConvertNestedNullability = conversion.Exists; } - isNullableIfReferenceType = operandType?.IsNullable; + isNullableIfReferenceType = operandType.IsNull ? null : operandType.IsNullable; } break; @@ -3105,7 +3109,7 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node) //if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability? { - _resultType = null; + _resultType = default; } return null; @@ -3194,7 +3198,7 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) ReportNullReferenceAssignmentIfNecessary(right, leftType, rightType, UseLegacyWarnings(left)); if (!canConvertNestedNullability) { - ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInAssignment, right.Syntax, GetTypeAsDiagnosticArgument(rightResult?.TypeSymbol), leftType.TypeSymbol); + ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInAssignment, right.Syntax, GetTypeAsDiagnosticArgument(rightResult.TypeSymbol), leftType.TypeSymbol); } TrackNullableStateForAssignment(right, leftType, MakeSlot(left), rightType, MakeSlot(right)); // PROTOTYPE(NullableReferenceTypes): Check node.Type.IsErrorType() instead? @@ -3325,7 +3329,7 @@ public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmen } else { - leftOnRightType = null; + leftOnRightType = default; } VisitRvalue(node.Right); @@ -3388,7 +3392,7 @@ public override BoundNode VisitAddressOfOperator(BoundAddressOfOperator node) /// private bool ReportNullReferenceArgumentIfNecessary(BoundExpression argument, TypeSymbolWithAnnotations argumentType, ParameterSymbol parameter, TypeSymbolWithAnnotations paramType) { - if (argumentType?.IsNullable == true) + if (argumentType.IsNullable == true) { if (paramType.IsReferenceType && paramType.IsNullable == false) { @@ -3410,7 +3414,7 @@ private void ReportArgumentWarnings(BoundExpression argument, TypeSymbolWithAnno ReportNullReferenceArgumentIfNecessary(argument, argumentType, parameter, paramType); - if ((object)argumentType != null && IsNullabilityMismatch(paramType.TypeSymbol, argumentType.TypeSymbol)) + if (!argumentType.IsNull && IsNullabilityMismatch(paramType.TypeSymbol, argumentType.TypeSymbol)) { ReportNullabilityMismatchInArgument(argument, argumentType.TypeSymbol, parameter, paramType.TypeSymbol); } @@ -3523,9 +3527,9 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node) foreach (var iterationVariable in node.IterationVariables) { int slot = GetOrCreateSlot(iterationVariable); - TypeSymbolWithAnnotations sourceType = node.EnumeratorInfoOpt?.ElementType; + TypeSymbolWithAnnotations sourceType = node.EnumeratorInfoOpt?.ElementType ?? default; bool? isNullableIfReferenceType = null; - if ((object)sourceType != null) + if (!sourceType.IsNull) { TypeSymbolWithAnnotations destinationType = iterationVariable.Type; HashSet useSiteDiagnostics = null; @@ -3579,7 +3583,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node) Debug.Assert(!IsConditionalState); var result = base.VisitUnaryOperator(node); - TypeSymbolWithAnnotations resultType = null; + TypeSymbolWithAnnotations resultType = default; // PROTOTYPE(NullableReferenceTypes): Update method based on inferred operand type. if (node.OperatorKind.IsUserDefined()) @@ -3595,7 +3599,7 @@ public override BoundNode VisitUnaryOperator(BoundUnaryOperator node) } } - _resultType = resultType ?? TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: null); + _resultType = resultType.IsNull ? TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: null) : resultType; return null; } @@ -3648,7 +3652,7 @@ private TypeSymbolWithAnnotations InferResultNullability(BoundUserDefinedConditi } else { - return null; + return default; } } @@ -3764,9 +3768,7 @@ public override BoundNode VisitFieldInfo(BoundFieldInfo node) public override BoundNode VisitDefaultExpression(BoundDefaultExpression node) { var result = base.VisitDefaultExpression(node); - _resultType = (object)node.Type == null ? - null : - TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: true); + _resultType = TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: true); return result; } @@ -3813,7 +3815,7 @@ public override BoundNode VisitAsOperator(BoundAsOperator node) case ConversionKind.Identity: case ConversionKind.ImplicitReference: // Inherit nullability from the operand - isNullable = _resultType?.IsNullable; + isNullable = _resultType.IsNullable; break; case ConversionKind.Boxing: @@ -3854,7 +3856,7 @@ public override BoundNode VisitSuppressNullableWarningExpression(BoundSuppressNu //if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability? { - _resultType = _resultType?.WithTopLevelNonNullabilityForReferenceTypes(); + _resultType = _resultType.IsNull ? default : _resultType.WithTopLevelNonNullabilityForReferenceTypes(); } return null; @@ -4048,7 +4050,7 @@ public override BoundNode VisitArrayInitialization(BoundArrayInitialization node private void SetUnknownResultNullability() { - _resultType = null; + _resultType = default; } public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation node) @@ -4081,12 +4083,12 @@ private void CheckPossibleNullReceiver(BoundExpression receiverOpt, bool checkTy if (receiverOpt != null && this.State.Reachable) { #if DEBUG - Debug.Assert(receiverOpt.Type is null || _resultType?.TypeSymbol is null || AreCloseEnough(receiverOpt.Type, _resultType.TypeSymbol)); + Debug.Assert(receiverOpt.Type is null || _resultType.TypeSymbol is null || AreCloseEnough(receiverOpt.Type, _resultType.TypeSymbol)); #endif - TypeSymbol receiverType = receiverOpt.Type ?? _resultType?.TypeSymbol; + TypeSymbol receiverType = receiverOpt.Type ?? _resultType.TypeSymbol; if ((object)receiverType != null && (!checkType || receiverType.IsReferenceType || receiverType.IsUnconstrainedTypeParameter()) && - (_resultType?.IsNullable == true || receiverType.IsUnconstrainedTypeParameter())) + (_resultType.IsNullable == true || receiverType.IsUnconstrainedTypeParameter())) { ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullReferenceReceiver, receiverOpt.Syntax); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs index d179dbe24fc3d..352e0e99c31c0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LambdaRewriter/LambdaCapturedVariable.cs @@ -23,7 +23,7 @@ private LambdaCapturedVariable(SynthesizedContainer frame, TypeSymbolWithAnnotat isReadOnly: false, isStatic: false) { - Debug.Assert((object)type != null); + Debug.Assert(!type.IsNull); // lifted fields do not need to have the CompilerGeneratedAttribute attached to it, the closure is already // marked as being compiler generated. diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index cddb6fa757c06..18e6a8418e752 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -213,7 +213,7 @@ private Conversion RewriteConversion(Conversion conversion) public sealed override TypeSymbol VisitType(TypeSymbol type) { - return TypeMap.SubstituteType(type)?.TypeSymbol; + return TypeMap.SubstituteType(type).TypeSymbol; } public override BoundNode VisitMethodInfo(BoundMethodInfo node) diff --git a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index ff50a2fe17d1b..c562598996509 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -165,7 +165,7 @@ internal override TypeSymbol IteratorElementType { if (_iteratorElementType == null) { - _iteratorElementType = TypeMap.SubstituteType(BaseMethod.IteratorElementType)?.TypeSymbol; + _iteratorElementType = TypeMap.SubstituteType(BaseMethod.IteratorElementType).TypeSymbol; } return _iteratorElementType; } diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index 509df4f7c2b44..ed31a774b8309 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -29,7 +29,7 @@ private void VisitTypeSymbolWithAnnotations(TypeSymbolWithAnnotations type, Abst public override void VisitArrayType(IArrayTypeSymbol symbol) { - VisitArrayType(symbol, typeOpt: null); + VisitArrayType(symbol, typeOpt: default); } private void VisitArrayType(IArrayTypeSymbol symbol, TypeSymbolWithAnnotations typeOpt) @@ -57,15 +57,15 @@ private void VisitArrayType(IArrayTypeSymbol symbol, TypeSymbolWithAnnotations t return; } - TypeSymbolWithAnnotations underlyingNonArrayTypeWithAnnotations = (symbol as ArrayTypeSymbol)?.ElementType; + TypeSymbolWithAnnotations underlyingNonArrayTypeWithAnnotations = (symbol as ArrayTypeSymbol)?.ElementType ?? default; var underlyingNonArrayType = symbol.ElementType; while (underlyingNonArrayType.Kind == SymbolKind.ArrayType) { - underlyingNonArrayTypeWithAnnotations = (underlyingNonArrayType as ArrayTypeSymbol)?.ElementType; + underlyingNonArrayTypeWithAnnotations = (underlyingNonArrayType as ArrayTypeSymbol)?.ElementType ?? default; underlyingNonArrayType = ((IArrayTypeSymbol)underlyingNonArrayType).ElementType; } - if ((object)underlyingNonArrayTypeWithAnnotations != null) + if (!underlyingNonArrayTypeWithAnnotations.IsNull) { VisitTypeSymbolWithAnnotations(underlyingNonArrayTypeWithAnnotations); } @@ -85,14 +85,14 @@ private void VisitArrayType(IArrayTypeSymbol symbol, TypeSymbolWithAnnotations t AddArrayRank(arrayType); AddNullableAnnotations(typeOpt); - typeOpt = (arrayType as ArrayTypeSymbol)?.ElementType; + typeOpt = (arrayType as ArrayTypeSymbol)?.ElementType ?? default; arrayType = arrayType.ElementType as IArrayTypeSymbol; } } private void AddNullableAnnotations(TypeSymbolWithAnnotations typeOpt) { - if (ReferenceEquals(typeOpt, null)) + if (typeOpt.IsNull) { return; } @@ -731,7 +731,7 @@ private void AddTypeArguments(ISymbol owner, ImmutableArray new AnonymousTypeField(f.Name, Location.None, (TypeSymbolWithAnnotations)null)); + var fields = key.Fields.SelectAsArray(f => new AnonymousTypeField(f.Name, Location.None, default)); var typeDescr = new AnonymousTypeDescriptor(fields, Location.None); return new AnonymousTypeTemplateSymbol(this, typeDescr); } diff --git a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs index 79823ace8eb4a..e9e5275f0ba59 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs @@ -22,7 +22,7 @@ private ArrayTypeSymbol( TypeSymbolWithAnnotations elementType, NamedTypeSymbol array) { - Debug.Assert((object)elementType != null); + Debug.Assert(!elementType.IsNull); Debug.Assert((object)array != null); _elementType = elementType; @@ -390,7 +390,7 @@ internal override bool ApplyNullableTransforms(ImmutableArray transforms, return false; } - if ((object)oldElementType == newElementType) + if (oldElementType.IsSameAs(newElementType)) { result = this; } @@ -409,7 +409,7 @@ internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() TypeSymbolWithAnnotations oldElementType = ElementType; TypeSymbolWithAnnotations newElementType = oldElementType.SetUnknownNullabilityForReferenceTypes(); - if ((object)oldElementType == newElementType) + if (oldElementType.IsSameAs(newElementType)) { return this; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs index a11fad4ac1412..fa33cb6ca1245 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs @@ -454,7 +454,7 @@ private static bool PermissionSetAttributeTypeHasRequiredProperty(NamedTypeSymbo if (members.Length == 1 && members[0].Kind == SymbolKind.Property) { var property = (PropertySymbol)members[0]; - if ((object)property.Type != null && property.Type.SpecialType == SpecialType.System_String && + if (!property.Type.IsNull && property.Type.SpecialType == SpecialType.System_String && property.DeclaredAccessibility == Accessibility.Public && property.GetMemberArity() == 0 && (object)property.SetMethod != null && property.SetMethod.DeclaredAccessibility == Accessibility.Public) { diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index 0391bb7e43fb7..50e90ba61f576 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -514,7 +514,9 @@ public int GetHashCode(Symbol member) if (_considerReturnType && member.GetMemberArity() == 0 && (_typeComparison & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) == 0) // If it is generic, then type argument might be in return type. { - hash = Hash.Combine(member.GetTypeOrReturnType(), hash); + // PROTOTYPE(NullableReferenceTypes): TypeSymbolWithAnnotations.GetHashCode() + // throws ExceptionUtilities.Unreachable. + hash = Hash.Combine(member.GetTypeOrReturnType().GetHashCode(), hash); } // CONSIDER: modify hash for constraints? diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs index 64d85825c5c8f..123289750b876 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NullableTypeDecoder.cs @@ -18,7 +18,7 @@ internal static TypeSymbolWithAnnotations TransformType( EntityHandle targetSymbolToken, PEModuleSymbol containingModule) { - Debug.Assert((object)metadataType != null); + Debug.Assert(!metadataType.IsNull); ImmutableArray nullableTransformFlags; containingModule.Module.HasNullableAttribute(targetSymbolToken, out nullableTransformFlags); diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs index 71c134ae95eb5..6c4431b9f63f9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs @@ -88,8 +88,8 @@ internal PEEventSymbol( } } - TypeSymbol originalEventType = _eventType?.TypeSymbol; - if ((object)_eventType == null) + TypeSymbol originalEventType = _eventType.TypeSymbol; + if (_eventType.IsNull) { var metadataDecoder = new MetadataDecoder(moduleSymbol, containingType); originalEventType = metadataDecoder.GetTypeOfToken(eventType); diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs index 26bd2f6fe1b8f..167ebc3775b0e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs @@ -33,7 +33,7 @@ internal sealed class PEFieldSymbol : FieldSymbol private ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized; - private TypeSymbolWithAnnotations _lazyType; + private TypeSymbolWithAnnotations.Builder _lazyType; private int _lazyFixedSize; private NamedTypeSymbol _lazyFixedImplementationType; private PEEventSymbol _associatedEventOpt; @@ -203,7 +203,7 @@ internal void SetAssociatedEvent(PEEventSymbol eventSymbol) private void EnsureSignatureIsLoaded() { - if ((object)_lazyType == null) + if (_lazyType.IsNull) { var moduleSymbol = _containingType.ContainingPEModule; bool isVolatile; @@ -234,7 +234,7 @@ private void EnsureSignatureIsLoaded() type = TypeSymbolWithAnnotations.Create(new PointerTypeSymbol(TypeSymbolWithAnnotations.Create(fixedElementType))); } - Interlocked.CompareExchange(ref _lazyType, type, null); + _lazyType.InterlockedInitialize(type); } } @@ -272,7 +272,7 @@ private PEModuleSymbol ContainingPEModule internal override TypeSymbolWithAnnotations GetFieldType(ConsList fieldsBeingBound) { EnsureSignatureIsLoaded(); - return _lazyType; + return _lazyType.ToType(); } public override bool IsFixed diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index 929fa6d679d4b..ecfded1dc8be4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -199,7 +199,7 @@ private PEParameterSymbol( Debug.Assert((object)moduleSymbol != null); Debug.Assert((object)containingSymbol != null); Debug.Assert(ordinal >= 0); - Debug.Assert((object)type != null); + Debug.Assert(!type.IsNull); isBad = false; _moduleSymbol = moduleSymbol; diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs index d9f6c1b62f1e8..5bf9a5176cabb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs @@ -261,7 +261,7 @@ private ImmutableArray DecodeTypeArguments(ImmutableA { TypeSymbolWithAnnotations typeArg = typeArgs[i]; TypeSymbolWithAnnotations decoded = DecodeTypeInternal(typeArg); - anyDecoded |= !ReferenceEquals(decoded, typeArg); + anyDecoded |= !decoded.IsSameAs(typeArg); decodedArgs.Add(decoded); } @@ -279,7 +279,7 @@ private ArrayTypeSymbol DecodeArrayType(ArrayTypeSymbol type) { TypeSymbolWithAnnotations elementType = type.ElementType; TypeSymbolWithAnnotations decodedElementType = DecodeTypeInternal(elementType); - return ReferenceEquals(decodedElementType, elementType) + return decodedElementType.IsSameAs(elementType) ? type : type.WithElementType(decodedElementType); } diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs index b12f33199836f..bdfdee88a4216 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs @@ -756,7 +756,7 @@ public MethodSymbol Construct(params TypeSymbol[] typeArguments) /// public MethodSymbol Construct(ImmutableArray typeArguments) { - return Construct(typeArguments.SelectAsArray(a => (object)a == null ? null : TypeSymbolWithAnnotations.Create(a))); + return Construct(typeArguments.SelectAsArray(a => TypeSymbolWithAnnotations.Create(a))); } internal MethodSymbol Construct(ImmutableArray typeArguments) diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index 3f36d3a337269..b118131ad8773 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -81,10 +81,10 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m // This prevents constraint checking from failing for corresponding type parameters. var notInferredTypeParameters = PooledHashSet.GetInstance(); var typeParams = method.TypeParameters; - var typeArgsForConstraintsCheck = typeArgs.SelectAsArray(a => (object)a == null ? null : TypeSymbolWithAnnotations.Create(a)); + var typeArgsForConstraintsCheck = typeArgs.SelectAsArray(a => TypeSymbolWithAnnotations.Create(a)); for (int i = 0; i < typeArgsForConstraintsCheck.Length; i++) { - if ((object)typeArgsForConstraintsCheck[i] == null) + if (typeArgsForConstraintsCheck[i].IsNull) { firstNullInTypeArgs = i; var builder = ArrayBuilder.GetInstance(); @@ -93,7 +93,7 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m for (; i < typeArgsForConstraintsCheck.Length; i++) { var typeArg = typeArgsForConstraintsCheck[i]; - if ((object)typeArg == null) + if (typeArg.IsNull) { notInferredTypeParameters.Add(typeParams[i]); builder.Add(TypeSymbolWithAnnotations.Create(ErrorTypeSymbol.UnknownResultType)); diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 8272d3c175c4d..ff2a1bacf02d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -777,7 +777,7 @@ internal override bool ApplyNullableTransforms(ImmutableArray transforms, result = this; return false; } - else if ((object)oldTypeArgument != newTypeArgument) + else if (!oldTypeArgument.IsSameAs(newTypeArgument)) { allTypeArguments[i] = newTypeArgument; haveChanges = true; @@ -815,7 +815,7 @@ internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() { TypeSymbolWithAnnotations oldTypeArgument = allTypeArguments[i]; TypeSymbolWithAnnotations newTypeArgument = oldTypeArgument.SetUnknownNullabilityForReferenceTypes(); - if ((object)oldTypeArgument != newTypeArgument) + if (!oldTypeArgument.IsSameAs(newTypeArgument)) { allTypeArguments[i] = newTypeArgument; haveChanges = true; @@ -901,9 +901,9 @@ internal NamedTypeSymbol GetUnboundGenericTypeOrSelf() /// internal abstract bool HasCodeAnalysisEmbeddedAttribute { get; } - internal static readonly Func TypeSymbolIsNullFunction = type => (object)type == null; + internal static readonly Func TypeSymbolIsNullFunction = type => type.IsNull; - internal static readonly Func TypeSymbolIsErrorType = type => (object)type != null && type.IsErrorType(); + internal static readonly Func TypeSymbolIsErrorType = type => !type.IsNull && type.IsErrorType(); private NamedTypeSymbol ConstructWithoutModifiers(ImmutableArray typeArguments, bool unbound, INonNullTypesContext nonNullTypesContext) { @@ -913,19 +913,9 @@ private NamedTypeSymbol ConstructWithoutModifiers(ImmutableArray typ { modifiedArguments = default(ImmutableArray); } - else if (typeArguments.IsEmpty) - { - modifiedArguments = ImmutableArray.Empty; - } else { - var builder = ArrayBuilder.GetInstance(typeArguments.Length); - foreach (TypeSymbol t in typeArguments) - { - builder.Add((object)t == null ? null : TypeSymbolWithAnnotations.Create(nonNullTypesContext, t)); - } - - modifiedArguments = builder.ToImmutableAndFree(); + modifiedArguments = typeArguments.SelectAsArray((t, c) => t == null ? default : TypeSymbolWithAnnotations.Create(c, t), nonNullTypesContext); } return Construct(modifiedArguments, unbound); diff --git a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs index de9b448dabdfb..9aec5c21f75b9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs @@ -23,7 +23,7 @@ internal sealed partial class PointerTypeSymbol : TypeSymbol, IPointerTypeSymbol /// The type being pointed at. internal PointerTypeSymbol(TypeSymbolWithAnnotations pointedAtType) { - Debug.Assert((object)pointedAtType != null); + Debug.Assert(!pointedAtType.IsNull); _pointedAtType = pointedAtType; } @@ -265,7 +265,7 @@ internal override bool ApplyNullableTransforms(ImmutableArray transforms, return false; } - if ((object)oldPointedAtType == newPointedAtType) + if (oldPointedAtType.IsSameAs(newPointedAtType)) { result = this; } @@ -282,7 +282,7 @@ internal override TypeSymbol SetUnknownNullabilityForReferenceTypes() TypeSymbolWithAnnotations oldPointedAtType = PointedAtType; TypeSymbolWithAnnotations newPointedAtType = oldPointedAtType.SetUnknownNullabilityForReferenceTypes(); - if ((object)oldPointedAtType == newPointedAtType) + if (oldPointedAtType.IsSameAs(newPointedAtType)) { return this; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs index 5cd4043ee893e..2e9c525cd47ec 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingMethodSymbol.cs @@ -130,7 +130,7 @@ public override TypeSymbolWithAnnotations ReturnType { get { - if ((object)_lazyReturnType == null) + if (_lazyReturnType.IsNull) { _lazyReturnType = this.RetargetingTranslator.Retarget(_underlyingMethod.ReturnType, RetargetOptions.RetargetPrimitiveTypesByTypeCode, this.ContainingType); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingPropertySymbol.cs index ba73a70ba93fa..eb3a315e95b4c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingPropertySymbol.cs @@ -59,7 +59,7 @@ public override TypeSymbolWithAnnotations Type { get { - if ((object)_lazyType == null) + if (_lazyType.IsNull) { var type = this.RetargetingTranslator.Retarget(_underlyingProperty.Type, RetargetOptions.RetargetPrimitiveTypesByTypeCode); if (type.TypeSymbol.TryAsDynamicIfNoPia(this.ContainingType, out TypeSymbol asDynamic)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs index 9841883404f93..e6c3669a118f8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs @@ -515,7 +515,7 @@ public NamedTypeSymbol Retarget(NamedTypeSymbol type, RetargetOptions options) { var newArg = Retarget(arg, RetargetOptions.RetargetPrimitiveTypesByTypeCode); // generic instantiation is a signature - if (!anythingRetargeted && ((object)newArg != arg)) + if (!anythingRetargeted && !newArg.IsSameAs(arg)) { anythingRetargeted = true; } @@ -680,7 +680,7 @@ public ArrayTypeSymbol Retarget(ArrayTypeSymbol type) TypeSymbolWithAnnotations oldElement = type.ElementType; TypeSymbolWithAnnotations newElement = Retarget(oldElement, RetargetOptions.RetargetPrimitiveTypesByTypeCode); - if ((object)oldElement == newElement) + if (oldElement.IsSameAs(newElement)) { return type; } @@ -730,7 +730,7 @@ public PointerTypeSymbol Retarget(PointerTypeSymbol type) TypeSymbolWithAnnotations oldPointed = type.PointedAtType; TypeSymbolWithAnnotations newPointed = Retarget(oldPointed, RetargetOptions.RetargetPrimitiveTypesByTypeCode); - if ((object)oldPointed == newPointed) + if (oldPointed.IsSameAs(newPointed)) { return type; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 7b1ab4809801a..0742ba96a199b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// internal class GlobalExpressionVariable : SourceMemberFieldSymbol { - private TypeSymbolWithAnnotations _lazyType; + private TypeSymbolWithAnnotations.Builder _lazyType; private SyntaxReference _typeSyntax; internal GlobalExpressionVariable( @@ -62,9 +62,9 @@ internal override TypeSymbolWithAnnotations GetFieldType(ConsList f { Debug.Assert(fieldsBeingBound != null); - if ((object)_lazyType != null) + if (!_lazyType.IsNull) { - return _lazyType; + return _lazyType.ToType(); } var typeSyntax = TypeSyntax; @@ -79,12 +79,12 @@ internal override TypeSymbolWithAnnotations GetFieldType(ConsList f bool isVar; TypeSymbolWithAnnotations type = binder.BindTypeOrVarKeyword(typeSyntax, diagnostics, out isVar); - Debug.Assert((object)type != null || isVar); + Debug.Assert(!type.IsNull || isVar); if (isVar && !fieldsBeingBound.ContainsReference(this)) { InferFieldType(fieldsBeingBound, binder); - Debug.Assert((object)_lazyType != null); + Debug.Assert(!_lazyType.IsNull); } else { @@ -98,7 +98,7 @@ internal override TypeSymbolWithAnnotations GetFieldType(ConsList f } diagnostics.Free(); - return _lazyType; + return _lazyType.ToType(); } /// @@ -107,23 +107,23 @@ internal override TypeSymbolWithAnnotations GetFieldType(ConsList f /// private TypeSymbolWithAnnotations SetType(CSharpCompilation compilation, DiagnosticBag diagnostics, TypeSymbolWithAnnotations type) { - var originalType = _lazyType; + var originalType = _lazyType.DefaultType; // In the event that we race to set the type of a field, we should // always deduce the same type, unless the cached type is an error. Debug.Assert((object)originalType == null || originalType.IsErrorType() || - originalType.TypeSymbol == type.TypeSymbol); + originalType == type.TypeSymbol); - if ((object)Interlocked.CompareExchange(ref _lazyType, type, null) == null) + if (_lazyType.InterlockedInitialize(type)) { TypeChecks(type.TypeSymbol, diagnostics); compilation.DeclarationDiagnostics.AddRange(diagnostics); state.NotePartComplete(CompletionPart.Type); } - return _lazyType; + return _lazyType.ToType(); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 2ce22a2525960..49621d6f1d5c3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -24,12 +24,12 @@ internal sealed class LambdaSymbol : SourceMethodSymbol /// This symbol is used as the return type of a LambdaSymbol when we are interpreting /// lambda's body in order to infer its return type. /// - internal static readonly TypeSymbolWithAnnotations ReturnTypeIsBeingInferred = TypeSymbolWithAnnotations.Create(new UnsupportedMetadataTypeSymbol()); + internal static readonly TypeSymbol ReturnTypeIsBeingInferred = new UnsupportedMetadataTypeSymbol(); /// /// This symbol is used as the return type of a LambdaSymbol when we failed to infer its return type. /// - internal static readonly TypeSymbolWithAnnotations InferenceFailureReturnType = TypeSymbolWithAnnotations.Create(new UnsupportedMetadataTypeSymbol()); + internal static readonly TypeSymbol InferenceFailureReturnType = new UnsupportedMetadataTypeSymbol(); private static readonly TypeSymbolWithAnnotations UnknownReturnType = TypeSymbolWithAnnotations.Create(ErrorTypeSymbol.UnknownResultType); @@ -47,7 +47,7 @@ public LambdaSymbol( _messageID = unboundLambda.Data.MessageID; _syntax = unboundLambda.Syntax; _refKind = refKind; - _returnType = returnType ?? ReturnTypeIsBeingInferred; + _returnType = returnType.IsNull ? TypeSymbolWithAnnotations.CreateUnannotated(NonNullTypesUnusedContext.Instance, ReturnTypeIsBeingInferred) : returnType; _isSynthesized = unboundLambda.WasCompilerGenerated; _isAsync = unboundLambda.IsAsync; // No point in making this lazy. We are always going to need these soon after creation of the symbol. @@ -166,7 +166,7 @@ internal override ImmutableArray GetAppliedConditionalSymbols() public override bool ReturnsVoid { - get { return (object)this.ReturnType != null && this.ReturnType.SpecialType == SpecialType.System_Void; } + get { return !this.ReturnType.IsNull && this.ReturnType.SpecialType == SpecialType.System_Void; } } public override RefKind RefKind @@ -185,8 +185,8 @@ public override TypeSymbolWithAnnotations ReturnType // IDE might inspect the symbol and want to know the return type. internal void SetInferredReturnType(RefKind refKind, TypeSymbolWithAnnotations inferredReturnType) { - Debug.Assert((object)inferredReturnType != null); - Debug.Assert((object)_returnType == ReturnTypeIsBeingInferred); + Debug.Assert(!inferredReturnType.IsNull); + Debug.Assert((object)_returnType.TypeSymbol == ReturnTypeIsBeingInferred); _refKind = refKind; _returnType = inferredReturnType; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index e669a47caf452..a695f5ea256ef 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -208,7 +208,7 @@ public override TypeSymbolWithAnnotations ReturnType internal void ComputeReturnType() { - if ((object)_lazyReturnType != null) + if (!_lazyReturnType.IsNull) { return; } @@ -243,7 +243,7 @@ internal void ComputeReturnType() lock (_declarationDiagnostics) { - if ((object)_lazyReturnType != null) + if (!_lazyReturnType.IsNull) { diagnostics.Free(); return; @@ -254,7 +254,7 @@ internal void ComputeReturnType() } } - public override bool ReturnsVoid => ReturnType?.SpecialType == SpecialType.System_Void; + public override bool ReturnsVoid => ReturnType.SpecialType == SpecialType.System_Void; public override int Arity => TypeParameters.Length; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index 297469682a79a..15276ea5cec98 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -40,14 +40,14 @@ public sealed override Symbol AssociatedSymbol protected sealed override void MethodChecks(DiagnosticBag diagnostics) { - Debug.Assert(_lazyParameters.IsDefault == ((object)_lazyReturnType == null)); + Debug.Assert(_lazyParameters.IsDefault == _lazyReturnType.IsNull); // CONSIDER: currently, we're copying the custom modifiers of the event overridden // by this method's associated event (by using the associated event's type, which is // copied from the overridden event). It would be more correct to copy them from // the specific accessor that this method is overriding (as in SourceMemberMethodSymbol). - if ((object)_lazyReturnType == null) + if (_lazyReturnType.IsNull) { CSharpCompilation compilation = this.DeclaringCompilation; Debug.Assert(compilation != null); @@ -105,7 +105,7 @@ public sealed override bool ReturnsVoid get { LazyMethodChecks(); - Debug.Assert((object)_lazyReturnType != null); + Debug.Assert(!_lazyReturnType.IsNull); return base.ReturnsVoid; } } @@ -120,7 +120,7 @@ public sealed override TypeSymbolWithAnnotations ReturnType get { LazyMethodChecks(); - Debug.Assert((object)_lazyReturnType != null); + Debug.Assert(!_lazyReturnType.IsNull); return _lazyReturnType; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index 370d9a240ac5f..14a7e70b7cc3e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -27,7 +27,7 @@ internal class SourceLocalSymbol : LocalSymbol private readonly RefKind _refKind; private readonly TypeSyntax _typeSyntax; private readonly LocalDeclarationKind _declarationKind; - private TypeSymbolWithAnnotations _type; + private TypeSymbolWithAnnotations.Builder _type; /// /// Scope to which the local can "escape" via aliasing/ref assignment. @@ -100,7 +100,7 @@ internal Binder TypeSyntaxBinder // don't let the debugger force inference. internal new string GetDebuggerDisplay() { - return ((object)_type != null) + return !_type.IsNull ? base.GetDebuggerDisplay() : $"{this.Kind} ${this.Name}"; } @@ -285,7 +285,7 @@ public override TypeSymbolWithAnnotations Type { get { - if ((object)_type == null) + if (_type.IsNull) { #if DEBUG concurrentTypeResolutions++; @@ -295,7 +295,7 @@ public override TypeSymbolWithAnnotations Type SetType(localType); } - return _type; + return _type.ToType(); } } @@ -336,7 +336,7 @@ private TypeSymbolWithAnnotations GetTypeSymbol() // If we got a valid result that was not void then use the inferred type // else create an error type. - if ((object)inferredType != null && + if (!inferredType.IsNull && inferredType.SpecialType != SpecialType.System_Void) { declType = inferredType; @@ -347,7 +347,7 @@ private TypeSymbolWithAnnotations GetTypeSymbol() } } - Debug.Assert((object)declType != null); + Debug.Assert(!declType.IsNull); // // Note that we drop the diagnostics on the floor! That is because this code is invoked mainly in @@ -366,23 +366,23 @@ protected virtual TypeSymbolWithAnnotations InferTypeOfVarVariable(DiagnosticBag // TODO: this method must be overridden for pattern variables to bind the // expression or statement that is the nearest enclosing to the pattern variable's // declaration. That will cause the type of the pattern variable to be set as a side-effect. - return _type; + return _type.ToType(); } internal void SetType(TypeSymbolWithAnnotations newType) { - TypeSymbolWithAnnotations originalType = _type; + TypeSymbol originalType = _type.DefaultType; // In the event that we race to set the type of a local, we should // always deduce the same type, or deduce that the type is an error. Debug.Assert((object)originalType == null || originalType.IsErrorType() && newType.IsErrorType() || - originalType.TypeSymbol == newType.TypeSymbol); + originalType == newType.TypeSymbol); if ((object)originalType == null) { - Interlocked.CompareExchange(ref _type, newType, null); + _type.InterlockedInitialize(newType); } } @@ -522,7 +522,9 @@ public LocalWithInitializer( protected override TypeSymbolWithAnnotations InferTypeOfVarVariable(DiagnosticBag diagnostics) { var initializerOpt = this._initializerBinder.BindInferredVariableInitializer(diagnostics, RefKind, _initializer, _initializer); - return initializerOpt?.GetTypeAndNullability(_initializer.IsFeatureStaticNullCheckingEnabled()); + return initializerOpt == null ? + default : + initializerOpt.GetTypeAndNullability(_initializer.IsFeatureStaticNullCheckingEnabled()); } internal override SyntaxNode ForbiddenZone => _initializer; @@ -675,8 +677,8 @@ protected override TypeSymbolWithAnnotations InferTypeOfVarVariable(DiagnosticBa throw ExceptionUtilities.UnexpectedValue(_deconstruction.Kind()); } - Debug.Assert((object)this._type != null); - return this._type; + Debug.Assert(!this._type.IsNull); + return _type.ToType(); } internal override SyntaxNode ForbiddenZone @@ -763,13 +765,13 @@ protected override TypeSymbolWithAnnotations InferTypeOfVarVariable(DiagnosticBa break; } - if ((object)this._type == null) + if (this._type.IsNull) { Debug.Assert(this.DeclarationKind == LocalDeclarationKind.DeclarationExpressionVariable); SetType(TypeSymbolWithAnnotations.Create(_nodeBinder.CreateErrorType("var"))); } - return this._type; + return _type.ToType(); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 0d14a14c3155c..a366f760971a0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -276,7 +276,7 @@ internal class SourceMemberFieldSymbolFromDeclarator : SourceMemberFieldSymbol { private readonly bool _hasInitializer; - private TypeSymbolWithAnnotations _lazyType; + private TypeSymbolWithAnnotations.Builder _lazyType; // Non-zero if the type of the field has been inferred from the type of its initializer expression // and the errors of binding the initializer have been or are being reported to compilation diagnostics. @@ -351,12 +351,12 @@ internal override bool HasPointerType { get { - if ((object)_lazyType != null) + if (!_lazyType.IsNull) { - Debug.Assert(_lazyType.TypeSymbol.IsPointerType() == + Debug.Assert(_lazyType.DefaultType.IsPointerType() == IsPointerFieldSyntactically()); - return _lazyType.TypeSymbol.IsPointerType(); + return _lazyType.DefaultType.IsPointerType(); } return IsPointerFieldSyntactically(); @@ -389,9 +389,9 @@ internal sealed override TypeSymbolWithAnnotations GetFieldType(ConsList 1) { @@ -460,7 +460,7 @@ internal sealed override TypeSymbolWithAnnotations GetFieldType(ConsList fieldsBeingBound) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 157e44a7c60fc..0f0c9f79285ae 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -37,7 +37,7 @@ internal sealed class SourcePropertySymbol : PropertySymbol, IAttributeTargetSym private SymbolCompletionState _state; private ImmutableArray _lazyParameters; - private TypeSymbolWithAnnotations _lazyType; + private TypeSymbolWithAnnotations.Builder _lazyType; /// /// Set in constructor, might be changed while decoding . @@ -238,7 +238,8 @@ private SourcePropertySymbol( // lazily since the property name depends on the metadata name of the base property, // and the property name is required to add the property to the containing type, and // the type and parameters are required to determine the override or implementation. - _lazyType = this.ComputeType(bodyBinder, syntax, diagnostics); + var type = this.ComputeType(bodyBinder, syntax, diagnostics); + _lazyType.InterlockedInitialize(type); _lazyParameters = this.ComputeParameters(bodyBinder, syntax, diagnostics); bool isOverride = false; @@ -270,11 +271,13 @@ private SourcePropertySymbol( // We do an extra check before copying the type to handle the case where the overriding // property (incorrectly) has a different type than the overridden property. In such cases, // we want to retain the original (incorrect) type to avoid hiding the type given in source. - if (_lazyType.TypeSymbol.Equals(overriddenPropertyType.TypeSymbol, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreDynamic)) + if (type.TypeSymbol.Equals(overriddenPropertyType.TypeSymbol, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreDynamic)) { - _lazyType = _lazyType.WithTypeAndModifiers( - CustomModifierUtils.CopyTypeCustomModifiers(overriddenPropertyType.TypeSymbol, _lazyType.TypeSymbol, this.ContainingAssembly, nonNullTypesContext: this), + type = type.WithTypeAndModifiers( + CustomModifierUtils.CopyTypeCustomModifiers(overriddenPropertyType.TypeSymbol, type.TypeSymbol, this.ContainingAssembly, nonNullTypesContext: this), overriddenPropertyType.CustomModifiers); + _lazyType = default; + _lazyType.InterlockedInitialize(type); } _lazyParameters = CustomModifierUtils.CopyParameterCustomModifiers(overriddenOrImplementedProperty.Parameters, _lazyParameters, alsoCopyParamsModifier: isOverride); @@ -471,20 +474,20 @@ public override TypeSymbolWithAnnotations Type { get { - if ((object)_lazyType == null) + if (_lazyType.IsNull) { var diagnostics = DiagnosticBag.GetInstance(); var binder = this.CreateBinderForTypeAndParameters(); var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); var result = this.ComputeType(binder, syntax, diagnostics); - if ((object)Interlocked.CompareExchange(ref _lazyType, result, null) == null) + if (_lazyType.InterlockedInitialize(result)) { this.AddDeclarationDiagnostics(diagnostics); } diagnostics.Free(); } - return _lazyType; + return _lazyType.ToType(); } } @@ -492,9 +495,9 @@ internal bool HasPointerType { get { - if ((object)_lazyType != null) + if (!_lazyType.IsNull) { - return _lazyType.IsPointerType(); + return _lazyType.DefaultType.IsPointerType(); } var syntax = (BasePropertyDeclarationSyntax)_syntaxRef.GetSyntax(); diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs index d53ab8ac1f503..49e1c69d08f08 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedEventSymbol.cs @@ -10,7 +10,7 @@ internal sealed class SubstitutedEventSymbol : WrappedEventSymbol { private readonly SubstitutedNamedTypeSymbol _containingType; - private TypeSymbolWithAnnotations _lazyType; + private TypeSymbolWithAnnotations.Builder _lazyType; internal SubstitutedEventSymbol(SubstitutedNamedTypeSymbol containingType, EventSymbol originalDefinition) : base(originalDefinition) @@ -23,12 +23,12 @@ public override TypeSymbolWithAnnotations Type { get { - if ((object)_lazyType == null) + if (_lazyType.IsNull) { - Interlocked.CompareExchange(ref _lazyType, _containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.Type), null); + _lazyType.InterlockedInitialize(_containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.Type)); } - return _lazyType; + return _lazyType.ToType(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs index 2e326df40649e..c4d77bb3b83c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedFieldSymbol.cs @@ -12,7 +12,7 @@ internal sealed class SubstitutedFieldSymbol : WrappedFieldSymbol { private readonly SubstitutedNamedTypeSymbol _containingType; - private TypeSymbolWithAnnotations _lazyType; + private TypeSymbolWithAnnotations.Builder _lazyType; internal SubstitutedFieldSymbol(SubstitutedNamedTypeSymbol containingType, FieldSymbol substitutedFrom) : base((FieldSymbol)substitutedFrom.OriginalDefinition) @@ -22,12 +22,12 @@ internal SubstitutedFieldSymbol(SubstitutedNamedTypeSymbol containingType, Field internal override TypeSymbolWithAnnotations GetFieldType(ConsList fieldsBeingBound) { - if ((object)_lazyType == null) + if (_lazyType.IsNull) { - Interlocked.CompareExchange(ref _lazyType, _containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.GetFieldType(fieldsBeingBound)), null); + _lazyType.InterlockedInitialize(_containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.GetFieldType(fieldsBeingBound))); } - return _lazyType; + return _lazyType.ToType(); } public override Symbol ContainingSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs index d3edf4e4ceb8e..0ed75eea49e4c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs @@ -22,7 +22,7 @@ internal class SubstitutedMethodSymbol : WrappedMethodSymbol private readonly TypeMap _inputMap; private readonly MethodSymbol _constructedFrom; - private TypeSymbolWithAnnotations _lazyReturnType; + private TypeSymbolWithAnnotations.Builder _lazyReturnType; private ImmutableArray _lazyParameters; private TypeMap _lazyMap; private ImmutableArray _lazyTypeParameters; @@ -230,14 +230,12 @@ public sealed override TypeSymbolWithAnnotations ReturnType { get { - var returnType = _lazyReturnType; - if ((object)returnType != null) + if (_lazyReturnType.IsNull) { - return returnType; + var returnType = Map.SubstituteTypeWithTupleUnification(OriginalDefinition.ReturnType); + _lazyReturnType.InterlockedInitialize(returnType); } - - returnType = Map.SubstituteTypeWithTupleUnification(OriginalDefinition.ReturnType); - return Interlocked.CompareExchange(ref _lazyReturnType, returnType, null) ?? returnType; + return _lazyReturnType.ToType(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs index aae49022367e7..b45361afc7619 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs @@ -45,24 +45,21 @@ public override TypeSymbolWithAnnotations Type get { var mapOrType = _mapOrType; - var type = mapOrType as TypeSymbolWithAnnotations; - if ((object)type != null) + if (mapOrType is TypeSymbolWithAnnotations type) { return type; } TypeSymbolWithAnnotations substituted = ((TypeMap)mapOrType).SubstituteTypeWithTupleUnification(this._underlyingParameter.Type); - type = substituted; - if (substituted.CustomModifiers.IsEmpty && this._underlyingParameter.Type.CustomModifiers.IsEmpty && this._underlyingParameter.RefCustomModifiers.IsEmpty) { - _mapOrType = type; + _mapOrType = substituted; } - return type; + return substituted; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs index 2ccb90dbb0e5e..2cff86934f85e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedPropertySymbol.cs @@ -10,7 +10,7 @@ internal sealed class SubstitutedPropertySymbol : WrappedPropertySymbol { private readonly SubstitutedNamedTypeSymbol _containingType; - private TypeSymbolWithAnnotations _lazyType; + private TypeSymbolWithAnnotations.Builder _lazyType; private ImmutableArray _lazyParameters; internal SubstitutedPropertySymbol(SubstitutedNamedTypeSymbol containingType, PropertySymbol originalDefinition) @@ -23,12 +23,12 @@ public override TypeSymbolWithAnnotations Type { get { - if ((object)_lazyType == null) + if (_lazyType.IsNull) { - Interlocked.CompareExchange(ref _lazyType, _containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.Type), null); + _lazyType.InterlockedInitialize(_containingType.TypeSubstitution.SubstituteTypeWithTupleUnification(OriginalDefinition.Type)); } - return _lazyType; + return _lazyType.ToType(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs index dba525a50b5c4..12cbe27892aff 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs @@ -58,7 +58,7 @@ public static bool Any(this ImmutableArray array, Sym for (int i = 0, n = array.Length; i < n; i++) { var item = array[i]; - if ((object)item != null && item.Kind == kind) + if (!item.IsNull && item.Kind == kind) { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs index e36cde76036eb..4083fb01c5321 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs @@ -15,9 +15,103 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// A simple class that combines a single type symbol with annotations /// [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] - internal abstract class TypeSymbolWithAnnotations : IFormattable + internal readonly struct TypeSymbolWithAnnotations : IFormattable { - public sealed override string ToString() => TypeSymbol.ToString(); + /// + /// A builder for lazy instances of TypeSymbolWithAnnotations. + /// + [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] + internal struct Builder + { + private TypeSymbol _defaultType; + private INonNullTypesContext _nonNullTypesContext; + private bool _isAnnotated; + private Extensions _extensions; + + /// + /// The underlying type, unless overridden by _extensions. + /// + internal TypeSymbol DefaultType => _defaultType; + + /// + /// True if the fields of the builder are unset. + /// + internal bool IsNull => _defaultType is null; + + /// + /// Set the fields of the builder. + /// + /// + /// This method guarantees: fields will be set once; exactly one caller is + /// returned true; and IsNull will return true until all fields are initialized. + /// This method does not guarantee that all fields will be set by the same + /// caller. Instead, the expectation is that all callers will attempt to initialize + /// the builder with equivalent TypeSymbolWithAnnotations instances where + /// different fields of the builder may be assigned from different instances. + /// + internal bool InterlockedInitialize(TypeSymbolWithAnnotations type) + { + if ((object)_defaultType != null) + { + return false; + } + _isAnnotated = type.IsAnnotated; + Interlocked.CompareExchange(ref _nonNullTypesContext, type.NonNullTypesContext, null); + Interlocked.CompareExchange(ref _extensions, type._extensions, null); + return (object)Interlocked.CompareExchange(ref _defaultType, type._defaultType, null) == null; + } + + /// + /// Create immutable TypeSymbolWithAnnotations instance. + /// + internal TypeSymbolWithAnnotations ToType() + { + return (object)_defaultType == null ? + default : + new TypeSymbolWithAnnotations(_defaultType, _nonNullTypesContext, _isAnnotated, _extensions); + } + + internal string GetDebuggerDisplay() => ToType().GetDebuggerDisplay(); + } + + /// + /// The underlying type, unless overridden by _extensions. + /// + private readonly TypeSymbol _defaultType; + + /// + /// Additional data or behavior. Such cases should be + /// uncommon to minimize allocations. + /// + private readonly Extensions _extensions; + + /// + /// Returns: + /// false for string, int, T; + /// true for string?, T? where T : class; and + /// true for int?, T? where T : struct. + /// + public readonly bool IsAnnotated; + + /// + /// [NonNullTypes] context used for determining whether unannotated types are not nullable. + /// Allows us to get the information without eagerly pulling on the NonNullTypes property (which causes cycles). + /// + public readonly INonNullTypesContext NonNullTypesContext; + + private TypeSymbolWithAnnotations(TypeSymbol defaultType, INonNullTypesContext nonNullTypesContext, bool isAnnotated, Extensions extensions) + { + Debug.Assert((object)defaultType != null); + Debug.Assert(!defaultType.IsNullableType() || isAnnotated); + Debug.Assert(nonNullTypesContext != null); + Debug.Assert(extensions != null); + _defaultType = defaultType; + IsAnnotated = isAnnotated; + NonNullTypesContext = nonNullTypesContext; + _extensions = extensions; + } + + public override string ToString() => TypeSymbol.ToString(); public string Name => TypeSymbol.Name; public SymbolKind Kind => TypeSymbol.Kind; @@ -64,10 +158,20 @@ public static TypeSymbolWithAnnotations Create(TypeSymbol typeSymbol, ImmutableA { if (typeSymbol is null) { - return null; + return default; } - return Create(typeSymbol, typeSymbol.IsNullableType(), customModifiers); + return CreateNonLazyType(typeSymbol, NonNullTypesTrueContext.Instance, isAnnotated: typeSymbol.IsNullableType(), customModifiers); + } + + internal static TypeSymbolWithAnnotations CreateNonLazyType(TypeSymbol typeSymbol, INonNullTypesContext nonNullTypesContext, bool isAnnotated, ImmutableArray customModifiers) + { + return new TypeSymbolWithAnnotations(typeSymbol, nonNullTypesContext, isAnnotated, Extensions.Create(customModifiers)); + } + + internal static TypeSymbolWithAnnotations CreateLazyNullableType(CSharpCompilation compilation, TypeSymbolWithAnnotations underlying) + { + return new TypeSymbolWithAnnotations(defaultType: underlying._defaultType, nonNullTypesContext: underlying.NonNullTypesContext, isAnnotated: true, Extensions.CreateLazy(compilation, underlying)); } // PROTOTYPE(NullableReferenceTypes): [Obsolete("Use explicit NonNullTypes context")] @@ -94,7 +198,7 @@ public static TypeSymbolWithAnnotations Create(TypeSymbol typeSymbol, INonNullTy if (typeSymbol is null) { - return null; + return default; } // PROTOTYPE(NullableReferenceTypes): See if the if/else can be simplified to: @@ -118,9 +222,14 @@ public static TypeSymbolWithAnnotations Create(TypeSymbol typeSymbol, INonNullTy isAnnotated = typeSymbol.IsNullableType(); } - return new NonLazyType(typeSymbol, nonNullTypesContext, isAnnotated: isAnnotated, customModifiers); + return CreateNonLazyType(typeSymbol, nonNullTypesContext, isAnnotated: isAnnotated, customModifiers); } + /// + /// True if the fields are unset. + /// + internal bool IsNull => _defaultType is null; + public TypeSymbolWithAnnotations SetIsAnnotated(CSharpCompilation compilation) { Debug.Assert(compilation.IsFeatureEnabled(MessageID.IDS_FeatureStaticNullChecking)); @@ -134,7 +243,7 @@ public TypeSymbolWithAnnotations SetIsAnnotated(CSharpCompilation compilation) { if (!typeSymbol.IsValueType) { - return new NonLazyType(typeSymbol, NonNullTypesContext, isAnnotated: true, this.CustomModifiers); + return CreateNonLazyType(typeSymbol, NonNullTypesContext, isAnnotated: true, this.CustomModifiers); } else { @@ -142,22 +251,28 @@ public TypeSymbolWithAnnotations SetIsAnnotated(CSharpCompilation compilation) } } - return new LazyNullableTypeParameter(compilation, this); + return CreateLazyNullableType(compilation, this); } /// /// Adjust types in signatures coming from metadata. /// - public abstract TypeSymbolWithAnnotations AsNullableReferenceType(); - public abstract TypeSymbolWithAnnotations AsNotNullableReferenceType(); + public TypeSymbolWithAnnotations AsNullableReferenceType() => _extensions.AsNullableReferenceType(this); + public TypeSymbolWithAnnotations AsNotNullableReferenceType() => _extensions.AsNotNullableReferenceType(this); + + public TypeSymbolWithAnnotations WithModifiers(ImmutableArray customModifiers) => + _extensions.WithModifiers(this, customModifiers); + public TypeSymbolWithAnnotations WithNonNullTypesContext(INonNullTypesContext nonNullTypesContext) => + _extensions.WithNonNullTypesContext(this, nonNullTypesContext); - public abstract TypeSymbolWithAnnotations WithModifiers(ImmutableArray customModifiers); - public abstract TypeSymbolWithAnnotations WithNonNullTypesContext(INonNullTypesContext nonNullTypesContext); + public TypeSymbol TypeSymbol => _extensions?.GetResolvedType(_defaultType); + public TypeSymbol NullableUnderlyingTypeOrSelf => _extensions.GetNullableUnderlyingTypeOrSelf(_defaultType); - public abstract TypeSymbol TypeSymbol { get; } - public virtual TypeSymbol NullableUnderlyingTypeOrSelf => TypeSymbol.StrippedType(); + // PROTOTYPE(NullableReferenceTypes): IsNullable depends on IsValueType which + // can lead to cycles when IsNullable is queried early. Replace this property with + // the Annotation property that depends on IsAnnotated and NonNullTypes only. + // Should review all the usages of IsNullable outside of NullableWalker. - // PROTOTYPE(NullableReferenceTypes): Should review all the usages of IsNullable outside of NullableWalker. /// /// Returns: /// true if this is a nullable reference or value type; @@ -169,21 +284,29 @@ public TypeSymbolWithAnnotations SetIsAnnotated(CSharpCompilation compilation) /// If this is a nullable reference type, /// simply returns a symbol for the reference type. /// - public abstract bool? IsNullable { get; } - - /// - /// Returns: - /// false for string, int, T; - /// true for string?, T? where T : class; and - /// true for int?, T? where T : struct. - /// - public abstract bool IsAnnotated { get; } - - /// - /// [NonNullTypes] context used for determining whether unannotated types are not nullable. - /// Allows us to get the information without eagerly pulling on the NonNullTypes property (which causes cycles). - /// - public abstract INonNullTypesContext NonNullTypesContext { get; } + public bool? IsNullable + { + get + { + if (_defaultType is null) + { + return null; + } + if (IsAnnotated) + { + return true; + } + if (NonNullTypesContext.NonNullTypes == true) + { + return false; + } + if (TypeSymbol.IsValueType) + { + return false; + } + return null; + } + } /// /// Returns: @@ -200,13 +323,20 @@ internal bool? IsAnnotatedWithNonNullTypesContext { get { + if (_defaultType is null) + { + return null; + } if (IsAnnotated) { return true; } - // A null NonNullTypes (ie. no attribute) means the same as NonNullTypes(false). - return NonNullTypesContext.NonNullTypes == true ? false : (bool?)null; + if (NonNullTypesContext.NonNullTypes == true) + { + return false; + } + return null; } } @@ -218,30 +348,34 @@ internal bool? IsAnnotatedWithNonNullTypesContext /// /// The list of custom modifiers, if any, associated with the . /// - public abstract ImmutableArray CustomModifiers { get; } + public ImmutableArray CustomModifiers => _extensions.CustomModifiers; public bool IsReferenceType => TypeSymbol.IsReferenceType; public bool IsValueType => TypeSymbol.IsValueType; public TypeKind TypeKind => TypeSymbol.TypeKind; - public virtual SpecialType SpecialType => TypeSymbol.SpecialType; + public SpecialType SpecialType => _extensions.GetSpecialType(_defaultType); public bool IsManagedType => TypeSymbol.IsManagedType; public Cci.PrimitiveTypeCode PrimitiveTypeCode => TypeSymbol.PrimitiveTypeCode; public bool IsEnumType() => TypeSymbol.IsEnumType(); public bool IsDynamic() => TypeSymbol.IsDynamic(); public bool IsObjectType() => TypeSymbol.IsObjectType(); public bool IsArray() => TypeSymbol.IsArray(); - public virtual bool IsRestrictedType(bool ignoreSpanLikeTypes = false) => TypeSymbol.IsRestrictedType(ignoreSpanLikeTypes); + public bool IsRestrictedType(bool ignoreSpanLikeTypes = false) => + _extensions.IsRestrictedType(_defaultType, ignoreSpanLikeTypes); public bool IsPointerType() => TypeSymbol.IsPointerType(); public bool IsErrorType() => TypeSymbol.IsErrorType(); public bool IsUnsafe() => TypeSymbol.IsUnsafe(); - public virtual bool IsStatic => TypeSymbol.IsStatic; + public bool IsStatic => _extensions.IsStatic(_defaultType); public bool IsNullableTypeOrTypeParameter() => TypeSymbol.IsNullableTypeOrTypeParameter(); - public virtual bool IsVoid => TypeSymbol.SpecialType == SpecialType.System_Void; - public virtual bool IsSZArray() => TypeSymbol.IsSZArray(); - public TypeSymbolWithAnnotations GetNullableUnderlyingType() => TypeSymbol.GetNullableUnderlyingTypeWithAnnotations(); + public bool IsVoid => _extensions.IsVoid(_defaultType); + public bool IsSZArray() => _extensions.IsSZArray(_defaultType); + public TypeSymbolWithAnnotations GetNullableUnderlyingType() => + TypeSymbol.GetNullableUnderlyingTypeWithAnnotations(); - internal abstract bool GetIsReferenceType(ConsList inProgress); - internal abstract bool GetIsValueType(ConsList inProgress); + internal bool GetIsReferenceType(ConsList inProgress) => + _extensions.GetIsReferenceType(_defaultType, inProgress); + internal bool GetIsValueType(ConsList inProgress) => + _extensions.GetIsValueType(_defaultType, inProgress); public string ToDisplayString(SymbolDisplayFormat format = null) { @@ -267,7 +401,7 @@ public string ToDisplayString(SymbolDisplayFormat format = null) return str; } - internal string GetDebuggerDisplay() => ToDisplayString(DebuggerDisplayFormat); + internal string GetDebuggerDisplay() => _defaultType is null ? "null" : ToDisplayString(DebuggerDisplayFormat); // PROTOTYPE(NullableReferenceTypes): Remove IFormattable implementation // if instances should not be used as Diagnostic arguments. @@ -278,12 +412,12 @@ string IFormattable.ToString(string format, IFormatProvider formatProvider) public bool Equals(TypeSymbolWithAnnotations other, TypeCompareKind comparison) { - if (ReferenceEquals(this, other)) + if (this.IsSameAs(other)) { return true; } - if ((object)other == null || !TypeSymbolEquals(other, comparison)) + if (other.IsNull || !TypeSymbolEquals(other, comparison)) { return false; } @@ -322,7 +456,7 @@ private EqualsComparer() public override int GetHashCode(TypeSymbolWithAnnotations obj) { - if (obj is null) + if (obj.IsNull) { return 0; } @@ -331,18 +465,16 @@ public override int GetHashCode(TypeSymbolWithAnnotations obj) public override bool Equals(TypeSymbolWithAnnotations x, TypeSymbolWithAnnotations y) { - if (x is null) + if (x.IsNull) { - return y is null; + return y.IsNull; } return x.Equals(y, TypeCompareKind.CompareNullableModifiersForReferenceTypes); } } - protected virtual bool TypeSymbolEquals(TypeSymbolWithAnnotations other, TypeCompareKind comparison) - { - return this.TypeSymbol.Equals(other.TypeSymbol, comparison); - } + internal bool TypeSymbolEquals(TypeSymbolWithAnnotations other, TypeCompareKind comparison) => + _extensions.TypeSymbolEquals(this, other, comparison); public bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet checkedTypes) { @@ -361,24 +493,19 @@ public bool IsAtLeastAsVisibleAs(Symbol sym, ref HashSet useSite return NullableUnderlyingTypeOrSelf.IsAtLeastAsVisibleAs(sym, ref useSiteDiagnostics); } - public TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap) - { - return SubstituteType(typeMap, withTupleUnification: false); - } - - public TypeSymbolWithAnnotations SubstituteTypeWithTupleUnification(AbstractTypeMap typeMap) - { - return SubstituteType(typeMap, withTupleUnification: true); - } + public TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap) => + _extensions.SubstituteType(this, typeMap, withTupleUnification: false); + public TypeSymbolWithAnnotations SubstituteTypeWithTupleUnification(AbstractTypeMap typeMap) => + _extensions.SubstituteType(this, typeMap, withTupleUnification: true); - protected virtual TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap, bool withTupleUnification) + internal TypeSymbolWithAnnotations SubstituteTypeCore(AbstractTypeMap typeMap, bool withTupleUnification) { var newCustomModifiers = typeMap.SubstituteCustomModifiers(this.CustomModifiers); var newTypeWithModifiers = typeMap.SubstituteType(this.TypeSymbol, withTupleUnification); bool newIsAnnotated = this.IsAnnotated || newTypeWithModifiers.IsAnnotated; // PROTOTYPE(NullableReferenceTypes): Can we use Equals instead? - if (TypeSymbolEquals(newTypeWithModifiers, TypeCompareKind.CompareNullableModifiersForReferenceTypes) && + if (this.TypeSymbolEquals(newTypeWithModifiers, TypeCompareKind.CompareNullableModifiersForReferenceTypes) && newTypeWithModifiers.CustomModifiers.IsEmpty && newIsAnnotated == this.IsAnnotated && newCustomModifiers == this.CustomModifiers) @@ -400,14 +527,22 @@ protected virtual TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeM { return newTypeWithModifiers; } - return new NonLazyType( + return TypeSymbolWithAnnotations.CreateNonLazyType( newTypeWithModifiers.TypeSymbol, newTypeWithModifiers.NonNullTypesContext, isAnnotated: newIsAnnotated, newCustomModifiers.Concat(newTypeWithModifiers.CustomModifiers)); } - public virtual void ReportDiagnosticsIfObsolete(Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics) + public void ReportDiagnosticsIfObsolete(Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics) => + _extensions.ReportDiagnosticsIfObsolete(this, binder, syntax, diagnostics); + + internal bool TypeSymbolEqualsCore(TypeSymbolWithAnnotations other, TypeCompareKind comparison) + { + return TypeSymbol.Equals(other.TypeSymbol, comparison); + } + + internal void ReportDiagnosticsIfObsoleteCore(Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics) { binder.ReportDiagnosticsIfObsolete(diagnostics, TypeSymbol, syntax, hasBaseReceiver: false); } @@ -416,24 +551,15 @@ public virtual void ReportDiagnosticsIfObsolete(Binder binder, SyntaxNode syntax /// Extract type under assumption that there should be no custom modifiers or annotations. /// The method asserts otherwise. /// - public abstract TypeSymbol AsTypeSymbolOnly(); + public TypeSymbol AsTypeSymbolOnly() => _extensions.AsTypeSymbolOnly(_defaultType); /// /// Is this the given type parameter? /// - public abstract bool Is(TypeParameterSymbol other); + public bool Is(TypeParameterSymbol other) => _extensions.Is(_defaultType, other); - public TypeSymbolWithAnnotations WithTypeAndModifiers(TypeSymbol typeSymbol, ImmutableArray customModifiers) - { - // PROTOTYPE(NullableReferenceTypes): This method can cause cycles, since it pulls on NonNullTypes - // Once TypeSymbolWithAnnotations is a struct, we can probably skip this optimization altogether - //if (CustomModifiers != customModifiers || !TypeSymbol.Equals(typeSymbol, TypeCompareKind.AllAspects)) - //{ - // return DoUpdate(typeSymbol, customModifiers); - //} - - return new NonLazyType(typeSymbol, NonNullTypesContext, isAnnotated: IsAnnotated, customModifiers); - } + public TypeSymbolWithAnnotations WithTypeAndModifiers(TypeSymbol typeSymbol, ImmutableArray customModifiers) => + _extensions.WithTypeAndModifiers(this, typeSymbol, customModifiers); public bool ContainsNullableReferenceTypes() { @@ -552,7 +678,7 @@ public TypeSymbolWithAnnotations WithTopLevelNonNullabilityForReferenceTypes() return this; } - return new NonLazyType(typeSymbol, NonNullTypesTrueContext.Instance, isAnnotated: false, CustomModifiers); + return CreateNonLazyType(typeSymbol, NonNullTypesTrueContext.Instance, isAnnotated: false, CustomModifiers); } public TypeSymbolWithAnnotations SetUnknownNullabilityForReferenceTypes() @@ -564,7 +690,7 @@ public TypeSymbolWithAnnotations SetUnknownNullabilityForReferenceTypes() if (!typeSymbol.IsValueType) { typeSymbol = typeSymbol.SetUnknownNullabilityForReferenceTypes(); - return new NonLazyType(typeSymbol, NonNullTypesFalseContext.Instance, isAnnotated: false, CustomModifiers); + return CreateNonLazyType(typeSymbol, NonNullTypesFalseContext.Instance, isAnnotated: false, CustomModifiers); } } @@ -580,7 +706,7 @@ public TypeSymbolWithAnnotations SetUnknownNullabilityForReferenceTypes() #pragma warning disable CS0809 [Obsolete("Unsupported", error: true)] - public sealed override bool Equals(object other) + public override bool Equals(object other) #pragma warning restore CS0809 { throw ExceptionUtilities.Unreachable; @@ -588,146 +714,175 @@ public sealed override bool Equals(object other) #pragma warning disable CS0809 [Obsolete("Unsupported", error: true)] - public sealed override int GetHashCode() + public override int GetHashCode() #pragma warning restore CS0809 { throw ExceptionUtilities.Unreachable; } +#pragma warning disable CS0809 [Obsolete("Unsupported", error: true)] - public static bool operator ==(TypeSymbolWithAnnotations x, TypeSymbolWithAnnotations y) + public static bool operator ==(TypeSymbolWithAnnotations? x, TypeSymbolWithAnnotations? y) +#pragma warning restore CS0809 { throw ExceptionUtilities.Unreachable; } +#pragma warning disable CS0809 [Obsolete("Unsupported", error: true)] - public static bool operator !=(TypeSymbolWithAnnotations x, TypeSymbolWithAnnotations y) + public static bool operator !=(TypeSymbolWithAnnotations? x, TypeSymbolWithAnnotations? y) +#pragma warning restore CS0809 { throw ExceptionUtilities.Unreachable; } - [Obsolete("Unsupported", error: true)] - public static bool operator ==(Symbol x, TypeSymbolWithAnnotations y) + // Field-wise ReferenceEquals. + internal bool IsSameAs(TypeSymbolWithAnnotations other) { - throw ExceptionUtilities.Unreachable; + return ReferenceEquals(_defaultType, other._defaultType) && + ReferenceEquals(NonNullTypesContext, other.NonNullTypesContext) && + IsAnnotated == other.IsAnnotated && + ReferenceEquals(_extensions, other._extensions); } - [Obsolete("Unsupported", error: true)] - public static bool operator !=(Symbol x, TypeSymbolWithAnnotations y) + /// + /// Additional data or behavior beyond the core TypeSymbolWithAnnotations. + /// + private abstract class Extensions { - throw ExceptionUtilities.Unreachable; - } + internal static readonly Extensions Default = new NonLazyType(ImmutableArray.Empty); - [Obsolete("Unsupported", error: true)] - public static bool operator ==(TypeSymbolWithAnnotations x, Symbol y) - { - throw ExceptionUtilities.Unreachable; - } + internal static Extensions Create(ImmutableArray customModifiers) + { + if (customModifiers.IsEmpty) + { + return Default; + } + return new NonLazyType(customModifiers); + } - [Obsolete("Unsupported", error: true)] - public static bool operator !=(TypeSymbolWithAnnotations x, Symbol y) - { - throw ExceptionUtilities.Unreachable; + internal static Extensions CreateLazy(CSharpCompilation compilation, TypeSymbolWithAnnotations underlying) + { + return new LazyNullableTypeParameter(compilation, underlying); + } + + internal abstract TypeSymbol GetResolvedType(TypeSymbol defaultType); + internal abstract ImmutableArray CustomModifiers { get; } + + internal abstract TypeSymbolWithAnnotations AsNullableReferenceType(TypeSymbolWithAnnotations type); + internal abstract TypeSymbolWithAnnotations AsNotNullableReferenceType(TypeSymbolWithAnnotations type); + + internal abstract TypeSymbolWithAnnotations WithModifiers(TypeSymbolWithAnnotations type, ImmutableArray customModifiers); + internal abstract TypeSymbolWithAnnotations WithNonNullTypesContext(TypeSymbolWithAnnotations type, INonNullTypesContext nonNullTypesContext); + + internal abstract TypeSymbol GetNullableUnderlyingTypeOrSelf(TypeSymbol typeSymbol); + + internal abstract bool GetIsReferenceType(TypeSymbol typeSymbol, ConsList inProgress); + internal abstract bool GetIsValueType(TypeSymbol typeSymbol, ConsList inProgress); + + internal abstract TypeSymbol AsTypeSymbolOnly(TypeSymbol typeSymbol); + + internal abstract SpecialType GetSpecialType(TypeSymbol typeSymbol); + internal abstract bool IsRestrictedType(TypeSymbol typeSymbol, bool ignoreSpanLikeTypes); + internal abstract bool IsStatic(TypeSymbol typeSymbol); + internal abstract bool IsVoid(TypeSymbol typeSymbol); + internal abstract bool IsSZArray(TypeSymbol typeSymbol); + + internal abstract bool Is(TypeSymbol typeSymbol, TypeParameterSymbol other); + + internal abstract TypeSymbolWithAnnotations WithTypeAndModifiers(TypeSymbolWithAnnotations type, TypeSymbol typeSymbol, ImmutableArray customModifiers); + + internal abstract bool TypeSymbolEquals(TypeSymbolWithAnnotations type, TypeSymbolWithAnnotations other, TypeCompareKind comparison); + internal abstract TypeSymbolWithAnnotations SubstituteType(TypeSymbolWithAnnotations type, AbstractTypeMap typeMap, bool withTupleUnification); + internal abstract void ReportDiagnosticsIfObsolete(TypeSymbolWithAnnotations type, Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics); } - private sealed class NonLazyType : TypeSymbolWithAnnotations + private sealed class NonLazyType : Extensions { - private readonly TypeSymbol _typeSymbol; - private readonly bool _isAnnotated; private readonly ImmutableArray _customModifiers; - private readonly INonNullTypesContext _nonNullTypesContext; - public NonLazyType(TypeSymbol typeSymbol, INonNullTypesContext nonNullTypesContext, bool isAnnotated, ImmutableArray customModifiers) + public NonLazyType(ImmutableArray customModifiers) { - Debug.Assert((object)typeSymbol != null); Debug.Assert(!customModifiers.IsDefault); - Debug.Assert(!typeSymbol.IsNullableType() || isAnnotated); - Debug.Assert(nonNullTypesContext != null); - - _typeSymbol = typeSymbol; - _nonNullTypesContext = nonNullTypesContext; - _isAnnotated = isAnnotated; _customModifiers = customModifiers; } - public override TypeSymbol TypeSymbol => _typeSymbol; + internal override TypeSymbol GetResolvedType(TypeSymbol defaultType) => defaultType; + internal override ImmutableArray CustomModifiers => _customModifiers; - // PROTOTYPE(NullableReferenceTypes): IsNullable depends on IsValueType which - // can lead to cycles when IsNullable is queried early. Replace this property with - // the Annotation property that depends on IsAnnotated and NonNullTypes only. - public override bool? IsNullable - { - get - { - if (_isAnnotated) - { - return true; - } - if (NonNullTypesContext.NonNullTypes == true) - { - return false; - } - if (_typeSymbol.IsValueType) - { - return false; - } - return null; - } - } + internal override SpecialType GetSpecialType(TypeSymbol typeSymbol) => typeSymbol.SpecialType; + internal override bool IsRestrictedType(TypeSymbol typeSymbol, bool ignoreSpanLikeTypes) => typeSymbol.IsRestrictedType(ignoreSpanLikeTypes); + internal override bool IsStatic(TypeSymbol typeSymbol) => typeSymbol.IsStatic; + internal override bool IsVoid(TypeSymbol typeSymbol) => typeSymbol.SpecialType == SpecialType.System_Void; + internal override bool IsSZArray(TypeSymbol typeSymbol) => typeSymbol.IsSZArray(); - public override bool IsAnnotated => _isAnnotated; - public override ImmutableArray CustomModifiers => _customModifiers; - public override INonNullTypesContext NonNullTypesContext => _nonNullTypesContext; + internal override TypeSymbol GetNullableUnderlyingTypeOrSelf(TypeSymbol typeSymbol) => typeSymbol.StrippedType(); - internal override bool GetIsReferenceType(ConsList inProgress) + internal override bool GetIsReferenceType(TypeSymbol typeSymbol, ConsList inProgress) { - if (_typeSymbol.TypeKind == TypeKind.TypeParameter) + if (typeSymbol.TypeKind == TypeKind.TypeParameter) { - return ((TypeParameterSymbol)_typeSymbol).GetIsReferenceType(inProgress); + return ((TypeParameterSymbol)typeSymbol).GetIsReferenceType(inProgress); } - return _typeSymbol.IsReferenceType; + return typeSymbol.IsReferenceType; } - internal override bool GetIsValueType(ConsList inProgress) + internal override bool GetIsValueType(TypeSymbol typeSymbol, ConsList inProgress) { - if (_typeSymbol.TypeKind == TypeKind.TypeParameter) + if (typeSymbol.TypeKind == TypeKind.TypeParameter) { - return ((TypeParameterSymbol)_typeSymbol).GetIsValueType(inProgress); + return ((TypeParameterSymbol)typeSymbol).GetIsValueType(inProgress); } - return _typeSymbol.IsValueType; + return typeSymbol.IsValueType; } - public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray customModifiers) + internal override TypeSymbolWithAnnotations WithModifiers(TypeSymbolWithAnnotations type, ImmutableArray customModifiers) { - return new NonLazyType(_typeSymbol, NonNullTypesContext, _isAnnotated, customModifiers); + return TypeSymbolWithAnnotations.CreateNonLazyType(type._defaultType, type.NonNullTypesContext, type.IsAnnotated, customModifiers); } - public override TypeSymbolWithAnnotations WithNonNullTypesContext(INonNullTypesContext nonNullTypesContext) + internal override TypeSymbolWithAnnotations WithNonNullTypesContext(TypeSymbolWithAnnotations type, INonNullTypesContext nonNullTypesContext) { Debug.Assert(nonNullTypesContext != null); - return NonNullTypesContext == nonNullTypesContext ? - this : - new NonLazyType(_typeSymbol, nonNullTypesContext, _isAnnotated, _customModifiers); + return TypeSymbolWithAnnotations.CreateNonLazyType(type._defaultType, nonNullTypesContext, type.IsAnnotated, _customModifiers); } - public override TypeSymbol AsTypeSymbolOnly() => _typeSymbol; + internal override TypeSymbol AsTypeSymbolOnly(TypeSymbol typeSymbol) => typeSymbol; // PROTOTYPE(NullableReferenceTypes): Use WithCustomModifiers.Is() => false // and set IsNullable=null always for GetTypeParametersAsTypeArguments. - public override bool Is(TypeParameterSymbol other) => _typeSymbol.Equals(other, TypeCompareKind.CompareNullableModifiersForReferenceTypes) && _customModifiers.IsEmpty; + internal override bool Is(TypeSymbol typeSymbol, TypeParameterSymbol other) => + typeSymbol.Equals(other, TypeCompareKind.CompareNullableModifiersForReferenceTypes) && _customModifiers.IsEmpty; + + internal override TypeSymbolWithAnnotations WithTypeAndModifiers(TypeSymbolWithAnnotations type, TypeSymbol typeSymbol, ImmutableArray customModifiers) + { + return TypeSymbolWithAnnotations.CreateNonLazyType(typeSymbol, type.NonNullTypesContext, type.IsAnnotated, customModifiers); + } + + internal override TypeSymbolWithAnnotations AsNullableReferenceType(TypeSymbolWithAnnotations type) + { + return TypeSymbolWithAnnotations.CreateNonLazyType(type._defaultType, type.NonNullTypesContext, isAnnotated: true, _customModifiers); + } + + internal override TypeSymbolWithAnnotations AsNotNullableReferenceType(TypeSymbolWithAnnotations type) + { + var defaultType = type._defaultType; + return TypeSymbolWithAnnotations.CreateNonLazyType(defaultType, type.NonNullTypesContext, isAnnotated: defaultType.IsNullableType(), _customModifiers); + } + + internal override bool TypeSymbolEquals(TypeSymbolWithAnnotations type, TypeSymbolWithAnnotations other, TypeCompareKind comparison) + { + return type.TypeSymbolEqualsCore(other, comparison); + } - public override TypeSymbolWithAnnotations AsNullableReferenceType() + internal override TypeSymbolWithAnnotations SubstituteType(TypeSymbolWithAnnotations type, AbstractTypeMap typeMap, bool withTupleUnification) { - return _isAnnotated ? - this : - new NonLazyType(_typeSymbol, NonNullTypesContext, isAnnotated: true, _customModifiers); + return type.SubstituteTypeCore(typeMap, withTupleUnification); } - public override TypeSymbolWithAnnotations AsNotNullableReferenceType() + internal override void ReportDiagnosticsIfObsolete(TypeSymbolWithAnnotations type, Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics) { - return !_isAnnotated || _typeSymbol.IsNullableType() ? - this : - new NonLazyType(_typeSymbol, NonNullTypesContext, isAnnotated: false, _customModifiers); + type.ReportDiagnosticsIfObsoleteCore(binder, syntax, diagnostics); } } @@ -735,7 +890,7 @@ public override TypeSymbolWithAnnotations AsNotNullableReferenceType() /// Nullable type parameter. The underlying TypeSymbol is resolved /// lazily to avoid cycles when binding declarations. /// - private sealed class LazyNullableTypeParameter : TypeSymbolWithAnnotations + private sealed class LazyNullableTypeParameter : Extensions { private readonly CSharpCompilation _compilation; private readonly TypeSymbolWithAnnotations _underlying; @@ -751,154 +906,164 @@ public LazyNullableTypeParameter(CSharpCompilation compilation, TypeSymbolWithAn _underlying = underlying; } - public override bool? IsNullable => true; - public override bool IsAnnotated => true; - public override INonNullTypesContext NonNullTypesContext => _underlying.NonNullTypesContext; - public override bool IsVoid => false; - public override bool IsSZArray() => false; - public override bool IsStatic => false; + internal override bool IsVoid(TypeSymbol typeSymbol) => false; + internal override bool IsSZArray(TypeSymbol typeSymbol) => false; + internal override bool IsStatic(TypeSymbol typeSymbol) => false; - public override TypeSymbol TypeSymbol + private TypeSymbol GetResolvedType() { - get + if ((object)_resolved == null) { - if ((object)_resolved == null) + if (!_underlying.IsValueType) { - if (!_underlying.IsValueType) - { - _resolved = _underlying.TypeSymbol; - } - else - { - Interlocked.CompareExchange(ref _resolved, - _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(_underlying)), - null); - } + _resolved = _underlying.TypeSymbol; + } + else + { + Interlocked.CompareExchange(ref _resolved, + _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(_underlying)), + null); } - - return _resolved; } + + return _resolved; } - internal override bool GetIsReferenceType(ConsList inProgress) + internal override bool GetIsReferenceType(TypeSymbol typeSymbol, ConsList inProgress) { return _underlying.GetIsReferenceType(inProgress); } - internal override bool GetIsValueType(ConsList inProgress) + internal override bool GetIsValueType(TypeSymbol typeSymbol, ConsList inProgress) { return _underlying.GetIsValueType(inProgress); } - public override TypeSymbol NullableUnderlyingTypeOrSelf => _underlying.TypeSymbol; + internal override TypeSymbol GetNullableUnderlyingTypeOrSelf(TypeSymbol typeSymbol) => _underlying.TypeSymbol; - public override SpecialType SpecialType => SpecialType.None; + internal override SpecialType GetSpecialType(TypeSymbol typeSymbol) + { + var specialType = _underlying.SpecialType; + return specialType.IsValueType() ? SpecialType.None : specialType; + } - public override bool IsRestrictedType(bool ignoreSpanLikeTypes) => false; + internal override bool IsRestrictedType(TypeSymbol typeSymbol, bool ignoreSpanLikeTypes) => _underlying.IsRestrictedType(ignoreSpanLikeTypes); - public override TypeSymbol AsTypeSymbolOnly() + internal override TypeSymbol AsTypeSymbolOnly(TypeSymbol typeSymbol) { - Debug.Assert(TypeSymbol.IsNullableType() && CustomModifiers.IsEmpty); - return TypeSymbol; + var resolvedType = GetResolvedType(); + Debug.Assert(resolvedType.IsNullableType() && CustomModifiers.IsEmpty); + return resolvedType; } // PROTOTYPE(NullableReferenceTypes): This implementation looks // incorrect since a type parameter cannot be Nullable. - public override bool Is(TypeParameterSymbol other) + internal override bool Is(TypeSymbol typeSymbol, TypeParameterSymbol other) { if (!other.IsNullableType()) { return false; } - return TypeSymbol.Equals(other, TypeCompareKind.CompareNullableModifiersForReferenceTypes); + var resolvedType = GetResolvedType(); + return resolvedType.Equals(other, TypeCompareKind.CompareNullableModifiersForReferenceTypes); } - public override ImmutableArray CustomModifiers => ImmutableArray.Empty; + internal override TypeSymbol GetResolvedType(TypeSymbol defaultType) => GetResolvedType(); + internal override ImmutableArray CustomModifiers => ImmutableArray.Empty; - public override TypeSymbolWithAnnotations WithModifiers(ImmutableArray customModifiers) + internal override TypeSymbolWithAnnotations WithModifiers(TypeSymbolWithAnnotations type, ImmutableArray customModifiers) { - if (customModifiers.IsDefaultOrEmpty) + if (customModifiers.IsEmpty) { - return this; + return type; } - // It should be safe to force resolution - var typeSymbol = TypeSymbol; + var resolvedType = GetResolvedType(); + if (resolvedType.IsNullableType()) + { + return TypeSymbolWithAnnotations.Create(resolvedType, customModifiers); + } + + return TypeSymbolWithAnnotations.CreateNonLazyType(resolvedType, type.NonNullTypesContext, isAnnotated: true, customModifiers); + } + + internal override TypeSymbolWithAnnotations WithNonNullTypesContext(TypeSymbolWithAnnotations type, INonNullTypesContext nonNullTypesContext) + { + return TypeSymbolWithAnnotations.CreateLazyNullableType(_compilation, _underlying.WithNonNullTypesContext(nonNullTypesContext)); + } + + internal override TypeSymbolWithAnnotations WithTypeAndModifiers(TypeSymbolWithAnnotations type, TypeSymbol typeSymbol, ImmutableArray customModifiers) + { if (typeSymbol.IsNullableType()) { return TypeSymbolWithAnnotations.Create(typeSymbol, customModifiers); } - return new NonLazyType(typeSymbol, NonNullTypesContext, isAnnotated: IsAnnotated, customModifiers); + return TypeSymbolWithAnnotations.CreateNonLazyType(typeSymbol, type.NonNullTypesContext, isAnnotated: true, customModifiers); } - public override TypeSymbolWithAnnotations WithNonNullTypesContext(INonNullTypesContext nonNullTypesContext) + internal override TypeSymbolWithAnnotations AsNullableReferenceType(TypeSymbolWithAnnotations type) { - return _underlying.NonNullTypesContext == nonNullTypesContext ? - this : - new LazyNullableTypeParameter(_compilation, _underlying.WithNonNullTypesContext(nonNullTypesContext)); + return type; } - public override TypeSymbolWithAnnotations AsNullableReferenceType() => this; - - public override TypeSymbolWithAnnotations AsNotNullableReferenceType() + internal override TypeSymbolWithAnnotations AsNotNullableReferenceType(TypeSymbolWithAnnotations type) { if (!_underlying.TypeSymbol.IsValueType) { return _underlying; } - - return this; + return type; } - protected override TypeSymbolWithAnnotations SubstituteType(AbstractTypeMap typeMap, bool withTupleUnification) + internal override TypeSymbolWithAnnotations SubstituteType(TypeSymbolWithAnnotations type, AbstractTypeMap typeMap, bool withTupleUnification) { if ((object)_resolved != null) { - return base.SubstituteType(typeMap, withTupleUnification); + return type.SubstituteTypeCore(typeMap, withTupleUnification); } - var newUnderlying = _underlying.SubstituteType(typeMap, withTupleUnification); - if ((object)newUnderlying != this._underlying) + var newUnderlying = _underlying.SubstituteTypeCore(typeMap, withTupleUnification); + if (!newUnderlying.IsSameAs(this._underlying)) { - if ((newUnderlying.TypeSymbol.Equals(this._underlying.TypeSymbol, TypeCompareKind.AllAspects) || + if ((newUnderlying.TypeSymbol.Equals(this._underlying.TypeSymbol, TypeCompareKind.AllAspects) || newUnderlying.TypeSymbol is IndexedTypeParameterSymbolForOverriding) && newUnderlying.CustomModifiers.IsEmpty) { - return new LazyNullableTypeParameter(_compilation, newUnderlying); + return TypeSymbolWithAnnotations.CreateLazyNullableType(_compilation, newUnderlying); } - return base.SubstituteType(typeMap, withTupleUnification); + return type.SubstituteTypeCore(typeMap, withTupleUnification); } else { - return this; // substitution had no effect on the type or modifiers + return type; // substitution had no effect on the type or modifiers } } - public override void ReportDiagnosticsIfObsolete(Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics) + internal override void ReportDiagnosticsIfObsolete(TypeSymbolWithAnnotations type, Binder binder, SyntaxNode syntax, DiagnosticBag diagnostics) { if ((object)_resolved != null) { - base.ReportDiagnosticsIfObsolete(binder, syntax, diagnostics); + type.ReportDiagnosticsIfObsoleteCore(binder, syntax, diagnostics); } else { - diagnostics.Add(new LazyObsoleteDiagnosticInfo(this, binder.ContainingMemberOrLambda, binder.Flags), syntax.GetLocation()); + diagnostics.Add(new LazyObsoleteDiagnosticInfo(type, binder.ContainingMemberOrLambda, binder.Flags), syntax.GetLocation()); } } - protected override bool TypeSymbolEquals(TypeSymbolWithAnnotations other, TypeCompareKind comparison) + internal override bool TypeSymbolEquals(TypeSymbolWithAnnotations type, TypeSymbolWithAnnotations other, TypeCompareKind comparison) { - var otherLazy = other as LazyNullableTypeParameter; + var otherLazy = other._extensions as LazyNullableTypeParameter; if ((object)otherLazy != null) { return _underlying.TypeSymbolEquals(otherLazy._underlying, comparison); } - return base.TypeSymbolEquals(other, comparison); + return type.TypeSymbolEqualsCore(other, comparison); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index c56cf61408e24..4d46e41d581f0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -28,7 +28,7 @@ public SynthesizedParameterSymbolBase( RefKind refKind, string name = "") { - Debug.Assert((object)type != null); + Debug.Assert(!type.IsNull); Debug.Assert(name != null); Debug.Assert(ordinal >= 0); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs index f2305b14b17ef..ab8f8ee69d259 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs @@ -15,7 +15,7 @@ internal sealed class TypeSubstitutedLocalSymbol : LocalSymbol public TypeSubstitutedLocalSymbol(LocalSymbol originalVariable, TypeSymbolWithAnnotations type, Symbol containingSymbol) { Debug.Assert(originalVariable != null); - Debug.Assert((object)type != null); + Debug.Assert(!type.IsNull); Debug.Assert(containingSymbol != null); _originalVariable = originalVariable; diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs index 1db89d1d926a7..c8709b9a42495 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs @@ -219,7 +219,7 @@ internal TupleTypeSymbol WithUnderlyingType(NamedTypeSymbol newUnderlyingType) internal TupleTypeSymbol WithElementTypes(ImmutableArray newElementTypes) { Debug.Assert(_elementTypes.Length == newElementTypes.Length); - Debug.Assert(newElementTypes.All(t => (object)t != null)); + Debug.Assert(newElementTypes.All(t => !t.IsNull)); NamedTypeSymbol firstTupleType; NamedTypeSymbol chainedTupleType; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index d46dfafa4bd91..03d3804b1d9fa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -624,12 +624,12 @@ public virtual NamedTypeSymbol TupleUnderlyingType internal bool ContainsNullableReferenceTypes() { - return TypeSymbolWithAnnotations.ContainsNullableReferenceTypes(typeWithAnnotationsOpt: null, typeOpt: this); + return TypeSymbolWithAnnotations.ContainsNullableReferenceTypes(typeWithAnnotationsOpt: default, typeOpt: this); } internal bool ContainsAnnotatedUnconstrainedTypeParameter() { - return TypeSymbolWithAnnotations.ContainsAnnotatedUnconstrainedTypeParameter(typeWithAnnotationsOpt: null, typeOpt: this); + return TypeSymbolWithAnnotations.ContainsAnnotatedUnconstrainedTypeParameter(typeWithAnnotationsOpt: default, typeOpt: this); } internal abstract void AddNullableTransforms(ArrayBuilder transforms); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 6b9e8273c4010..f52f25dbf05eb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -545,7 +545,7 @@ public static TypeSymbol VisitType( bool canDigThroughNullable = false) { return VisitType( - typeWithAnnotationsOpt: null, + typeWithAnnotationsOpt: default, typeOpt: type, typeWithAnnotationsPredicateOpt: null, typePredicateOpt: predicate, @@ -572,7 +572,7 @@ public static TypeSymbol VisitType( T arg, bool canDigThroughNullable = false) { - Debug.Assert((typeWithAnnotationsOpt is null) != (typeOpt is null)); + Debug.Assert(typeWithAnnotationsOpt.IsNull != (typeOpt is null)); // In order to handle extremely "deep" types like "int[][][][][][][][][]...[]" // or int*****************...* we implement manual tail recursion rather than @@ -580,7 +580,7 @@ public static TypeSymbol VisitType( while (true) { - TypeSymbol current = typeOpt ?? typeWithAnnotationsOpt?.TypeSymbol; + TypeSymbol current = typeOpt ?? typeWithAnnotationsOpt.TypeSymbol; bool isNestedNamedType = false; // Visit containing types from outer-most to inner-most. @@ -596,7 +596,7 @@ public static TypeSymbol VisitType( if ((object)containingType != null) { isNestedNamedType = true; - var result = VisitType(null, containingType, typeWithAnnotationsPredicateOpt, typePredicateOpt, arg, canDigThroughNullable); + var result = VisitType(default, containingType, typeWithAnnotationsPredicateOpt, typePredicateOpt, arg, canDigThroughNullable); if ((object)result != null) { return result; @@ -610,7 +610,7 @@ public static TypeSymbol VisitType( break; } - if ((object)typeWithAnnotationsOpt != null && typeWithAnnotationsPredicateOpt != null) + if (!typeWithAnnotationsOpt.IsNull && typeWithAnnotationsPredicateOpt != null) { if (typeWithAnnotationsPredicateOpt(typeWithAnnotationsOpt, arg, isNestedNamedType)) { @@ -650,7 +650,7 @@ public static TypeSymbol VisitType( { // Let's try to avoid early resolution of nullable types var result = VisitType( - typeWithAnnotationsOpt: canDigThroughNullable ? null : typeArg, + typeWithAnnotationsOpt: canDigThroughNullable ? default : typeArg, typeOpt: canDigThroughNullable ? typeArg.NullableUnderlyingTypeOrSelf : null, typeWithAnnotationsPredicateOpt, typePredicateOpt, @@ -676,7 +676,7 @@ public static TypeSymbol VisitType( } // Let's try to avoid early resolution of nullable types - typeWithAnnotationsOpt = canDigThroughNullable ? null : next; + typeWithAnnotationsOpt = canDigThroughNullable ? default : next; typeOpt = canDigThroughNullable ? next.NullableUnderlyingTypeOrSelf : null; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeUnification.cs b/src/Compilers/CSharp/Portable/Symbols/TypeUnification.cs index 0a6201bd65198..9249b1ae70e5f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeUnification.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeUnification.cs @@ -21,8 +21,8 @@ public static bool CanUnify(TypeSymbol t1, TypeSymbol t2) } MutableTypeMap substitution = null; - bool result = CanUnifyHelper((object)t1 == null ? null : TypeSymbolWithAnnotations.Create(t1), - (object)t2 == null ? null : TypeSymbolWithAnnotations.Create(t2), + bool result = CanUnifyHelper(TypeSymbolWithAnnotations.Create(t1), + TypeSymbolWithAnnotations.Create(t2), ref substitution); #if DEBUG if (result && ((object)t1 != null && (object)t2 != null)) @@ -47,7 +47,7 @@ private static TypeSymbolWithAnnotations SubstituteAllTypeParameters(AbstractTyp { previous = type; type = type.SubstituteType(substitution); - } while ((object)type != previous); + } while (!type.IsSameAs(previous)); } return type; @@ -73,9 +73,9 @@ private static TypeSymbolWithAnnotations SubstituteAllTypeParameters(AbstractTyp /// private static bool CanUnifyHelper(TypeSymbolWithAnnotations t1, TypeSymbolWithAnnotations t2, ref MutableTypeMap substitution) { - if ((object)t1 == null || (object)t2 == null) + if (t1.IsNull || t2.IsNull) { - return (object)t1 == t2; + return t1.IsSameAs(t2); } if (t1.TypeSymbol == t2.TypeSymbol && t1.CustomModifiers.SequenceEqual(t2.CustomModifiers)) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index ad5caf69e9e8b..533821bb99407 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -23011,7 +23011,7 @@ static void Main() var model = comp.GetSemanticModel(tree); var x = tree.GetRoot().DescendantNodes().OfType().ElementAt(1); Assert.Equal("x", x.Identifier.ToString()); - var xSymbol = (model.GetDeclaredSymbol(x) as LocalSymbol)?.Type; + var xSymbol = ((LocalSymbol)model.GetDeclaredSymbol(x)).Type; Assert.Equal("(System.Int32 a, System.Int32 b)", xSymbol.ToTestDisplayString()); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs index 0d6659e3003fc..df63ff966b3fd 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs @@ -159,7 +159,7 @@ interface IAsyncResult {} Assert.Equal("System.IAsyncResult", beginInvoke.ReturnType.TypeSymbol.ToTestDisplayString()); for (int i = 0; i < invoke.Parameters.Length; i++) { - Assert.Equal(invoke.Parameters[i].Type, beginInvoke.Parameters[i].Type); + Assert.Equal(invoke.Parameters[i].Type.TypeSymbol, beginInvoke.Parameters[i].Type.TypeSymbol); Assert.Equal(invoke.Parameters[i].RefKind, beginInvoke.Parameters[i].RefKind); } var lastParameterType = beginInvoke.Parameters[invoke.Parameters.Length].Type.TypeSymbol; @@ -168,13 +168,13 @@ interface IAsyncResult {} Assert.Equal("System.Object", beginInvoke.Parameters[invoke.Parameters.Length + 1].Type.TypeSymbol.ToTestDisplayString()); var endInvoke = myDel.GetMembers("EndInvoke").Single() as MethodSymbol; - Assert.Equal(invoke.ReturnType, endInvoke.ReturnType); + Assert.Equal(invoke.ReturnType.TypeSymbol, endInvoke.ReturnType.TypeSymbol); int k = 0; for (int i = 0; i < invoke.Parameters.Length; i++) { if (invoke.Parameters[i].RefKind != RefKind.None) { - Assert.Equal(invoke.Parameters[i].Type, endInvoke.Parameters[k].Type); + Assert.Equal(invoke.Parameters[i].Type.TypeSymbol, endInvoke.Parameters[k].Type.TypeSymbol); Assert.Equal(invoke.Parameters[i].RefKind, endInvoke.Parameters[k++].RefKind); } } diff --git a/src/Compilers/Test/Utilities/CSharp/UsesIsNullableVisitor.cs b/src/Compilers/Test/Utilities/CSharp/UsesIsNullableVisitor.cs index f32edcff56f9f..7a096f8f10e3a 100644 --- a/src/Compilers/Test/Utilities/CSharp/UsesIsNullableVisitor.cs +++ b/src/Compilers/Test/Utilities/CSharp/UsesIsNullableVisitor.cs @@ -122,7 +122,7 @@ private bool AddIfUsesIsNullable(Symbol symbol, TypeSymbolWithAnnotations type) private bool UsesIsNullable(TypeSymbolWithAnnotations type) { - if (type is null) + if (type.IsNull) { return false; } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs index b6c548718d456..b531c44b29216 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs @@ -1305,7 +1305,8 @@ private static void GetDisplayClassVariables( var name = local.Name; if ((name != null) && (GeneratedNames.GetKind(name) == GeneratedNameKind.DisplayClassLocalOrField)) { - if (displayClassTypes.Add(local.Type?.TypeSymbol)) + var localType = local.Type.TypeSymbol; + if ((object)localType != null && displayClassTypes.Add(localType)) { var instance = new DisplayClassInstanceFromLocal((EELocalSymbol)local); displayClassInstances.Add(new DisplayClassInstanceAndFields(instance)); From 0ece26c7cb26aa6900bfc5ca495ae58d04c3b6fc Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 3 Aug 2018 07:36:07 -0700 Subject: [PATCH 12/12] Include nullability in CheckConstraints (#28959) --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 3 +- .../Semantics/Conversions/Conversions.cs | 13 +- .../Semantics/Conversions/ConversionsBase.cs | 32 +- .../Semantics/Conversions/TypeConversions.cs | 15 +- .../OverloadResolution/OverloadResolution.cs | 1 + .../Portable/BoundTree/UnboundLambda.cs | 2 +- .../Portable/CSharpResources.Designer.cs | 18 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Errors/ErrorFacts.cs | 1 + .../Portable/FlowAnalysis/NullableWalker.cs | 38 +- .../Generated/ErrorFacts.Generated.cs | 1 + .../Portable/Symbols/ConstraintsHelper.cs | 32 +- .../Symbols/MethodSymbolExtensions.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Attributes/AttributeTests_Nullable.cs | 78 +-- .../Semantics/NullableReferenceTypesTests.cs | 447 ++++++++++++++++-- .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 1 + 30 files changed, 736 insertions(+), 85 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index d4e0a02129e35..a80989af35998 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1166,7 +1166,8 @@ private NamedTypeSymbol ConstructNamedType( if (ShouldCheckConstraints) { - type.CheckConstraintsForNonTuple(this.Conversions, typeSyntax, typeArgumentsSyntax, this.Compilation, basesBeingResolved, diagnostics); + bool includeNullability = Compilation.IsFeatureEnabled(MessageID.IDS_FeatureStaticNullChecking); + type.CheckConstraintsForNonTuple(this.Conversions.WithNullability(includeNullability), typeSyntax, typeArgumentsSyntax, this.Compilation, basesBeingResolved, diagnostics); } type = (NamedTypeSymbol)TupleTypeSymbol.TransformToTupleIfCompatible(type); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 2ed12a6c27ec9..69f6ad273779b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -13,26 +13,27 @@ internal sealed class Conversions : ConversionsBase private readonly Binder _binder; public Conversions(Binder binder) - : this(binder, currentRecursionDepth: 0, includeNullability: false) + : this(binder, currentRecursionDepth: 0, includeNullability: false, otherNullabilityOpt: null) { } - private Conversions(Binder binder, int currentRecursionDepth, bool includeNullability) - : base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth, includeNullability) + private Conversions(Binder binder, int currentRecursionDepth, bool includeNullability, Conversions otherNullabilityOpt) + : base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth, includeNullability, otherNullabilityOpt) { _binder = binder; } protected override ConversionsBase CreateInstance(int currentRecursionDepth) { - return new Conversions(_binder, currentRecursionDepth, IncludeNullability); + return new Conversions(_binder, currentRecursionDepth, IncludeNullability, otherNullabilityOpt: null); } private CSharpCompilation Compilation { get { return _binder.Compilation; } } - internal Conversions WithNullability(bool includeNullability) + protected override ConversionsBase WithNullabilityCore(bool includeNullability) { - return (IncludeNullability == includeNullability) ? this : new Conversions(_binder, currentRecursionDepth, includeNullability); + Debug.Assert(IncludeNullability != includeNullability); + return new Conversions(_binder, currentRecursionDepth, includeNullability, this); } public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 32017d08dcdfc..1b0d270614b8b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -20,15 +20,45 @@ internal abstract partial class ConversionsBase internal readonly bool IncludeNullability; - protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability) + /// + /// An optional clone of this instance with distinct IncludeNullability. + /// Used to avoid unnecessary allocations when calling WithNullability() repeatedly. + /// + private ConversionsBase _lazyOtherNullability; + + protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability, ConversionsBase otherNullabilityOpt) { Debug.Assert((object)corLibrary != null); + Debug.Assert(otherNullabilityOpt == null || includeNullability != otherNullabilityOpt.IncludeNullability); + Debug.Assert(otherNullabilityOpt == null || currentRecursionDepth == otherNullabilityOpt.currentRecursionDepth); this.corLibrary = corLibrary; this.currentRecursionDepth = currentRecursionDepth; IncludeNullability = includeNullability; + _lazyOtherNullability = otherNullabilityOpt; } + /// + /// Returns this instance if includeNullability is correct, and returns a + /// cached clone of this instance with distinct includeNullability otherwise. + /// + internal ConversionsBase WithNullability(bool includeNullability) + { + if (IncludeNullability == includeNullability) + { + return this; + } + if (_lazyOtherNullability == null) + { + _lazyOtherNullability = WithNullabilityCore(includeNullability); + } + Debug.Assert(_lazyOtherNullability.IncludeNullability == includeNullability); + Debug.Assert(_lazyOtherNullability._lazyOtherNullability == this); + return _lazyOtherNullability; + } + + protected abstract ConversionsBase WithNullabilityCore(bool includeNullability); + public abstract Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics); public abstract Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref HashSet useSiteDiagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs index 284e00923be97..c9490059d762c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs @@ -4,24 +4,31 @@ using Roslyn.Utilities; using System; using System.Collections.Generic; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp { internal sealed class TypeConversions : ConversionsBase { public TypeConversions(AssemblySymbol corLibrary) - : this(corLibrary, currentRecursionDepth: 0, includeNullability: false) + : this(corLibrary, currentRecursionDepth: 0, includeNullability: false, otherNullabilityOpt: null) { } - private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability) - : base(corLibrary, currentRecursionDepth, includeNullability) + private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability, TypeConversions otherNullabilityOpt) + : base(corLibrary, currentRecursionDepth, includeNullability, otherNullabilityOpt) { } protected override ConversionsBase CreateInstance(int currentRecursionDepth) { - return new TypeConversions(this.corLibrary, currentRecursionDepth, IncludeNullability); + return new TypeConversions(this.corLibrary, currentRecursionDepth, IncludeNullability, otherNullabilityOpt: null); + } + + protected override ConversionsBase WithNullabilityCore(bool includeNullability) + { + Debug.Assert(IncludeNullability != includeNullability); + return new TypeConversions(corLibrary, currentRecursionDepth, includeNullability, this); } public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index b6ac63dcfe9a3..6bce354372692 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -386,6 +386,7 @@ private bool FailsConstraintChecks(MethodSymbol method, out ArrayBuilder + /// Looks up a localized string similar to The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'.. + /// + internal static string WRN_NullabilityMismatchInTypeParameterConstraint { + get { + return ResourceManager.GetString("WRN_NullabilityMismatchInTypeParameterConstraint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type.. + /// + internal static string WRN_NullabilityMismatchInTypeParameterConstraint_Title { + get { + return ResourceManager.GetString("WRN_NullabilityMismatchInTypeParameterConstraint_Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot convert null literal to non-nullable reference or unconstrained type parameter.. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index f2e35f9bc0a48..18f98fab1e667 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5363,6 +5363,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot use a nullable reference type in object creation. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f73e226bf75ae..da2b6239e124e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1615,6 +1615,7 @@ internal enum ErrorCode ERR_AnnotationDisallowedInObjectCreation = 8628, WRN_MissingNonNullTypesContext = 8629, ERR_NonNullTypesNotAvailable = 8630, + WRN_NullabilityMismatchInTypeParameterConstraint = 8631, } // Note: you will need to re-generate compiler code after adding warnings (build\scripts\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index ac771c16e8d51..412e076a3a835 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -342,6 +342,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate: case ErrorCode.WRN_NullAsNonNullable: case ErrorCode.WRN_NoBestNullabilityConditionalExpression: + case ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint: case ErrorCode.WRN_Experimental: case ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable: case ErrorCode.WRN_TupleBinopLiteralNameMismatch: diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index ac888fbf0b182..e99a0c384be2e 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -141,7 +141,7 @@ private NullableWalker( // If so, are we interested in an InMethodBinder specifically? _binder = compilation.GetBinderFactory(node.SyntaxTree).GetBinder(node.Syntax); Debug.Assert(!_binder.Conversions.IncludeNullability); - _conversions = _binder.Conversions.WithNullability(true); + _conversions = (Conversions)_binder.Conversions.WithNullability(true); _useMethodSignatureReturnType = (object)methodSignatureOpt != null && useMethodSignatureReturnType; _useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes; _methodSignatureOpt = methodSignatureOpt; @@ -2047,10 +2047,18 @@ private MethodSymbol VisitArguments( // We do a first pass to work through the arguments without making any assumptions ImmutableArray results = VisitArgumentsEvaluate(arguments, refKindsOpt); - if ((object)method != null && method.IsGenericMethod && HasImplicitTypeArguments(node)) + if ((object)method != null && method.IsGenericMethod) { - method = InferMethodTypeArguments((BoundCall)node, method, GetArgumentsForMethodTypeInference(arguments, results)); - parameters = method.Parameters; + if (HasImplicitTypeArguments(node)) + { + method = InferMethodTypeArguments((BoundCall)node, method, GetArgumentsForMethodTypeInference(arguments, results)); + parameters = method.Parameters; + } + if (!method.IsDefinition) + { + var syntax = node.Syntax; + CheckMethodConstraints((syntax as InvocationExpressionSyntax)?.Expression ?? syntax, method); + } } // PROTOTYPE(NullableReferenceTypes): Can we handle some error cases? @@ -2489,7 +2497,6 @@ private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol metho ref useSiteDiagnostics); if (result.Success) { - // PROTOTYPE(NullableReferenceTypes): Check constraints return definition.Construct(result.InferredTypeArguments); } return method; @@ -2532,6 +2539,27 @@ BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, Type } } + private void CheckMethodConstraints(SyntaxNode syntax, MethodSymbol method) + { + var diagnosticsBuilder = ArrayBuilder.GetInstance(); + var warningsBuilder = ArrayBuilder.GetInstance(); + ArrayBuilder useSiteDiagnosticsBuilder = null; + ConstraintsHelper.CheckMethodConstraints( + method, + _conversions, + compilation, + diagnosticsBuilder, + warningsBuilder, + ref useSiteDiagnosticsBuilder); + foreach (var pair in warningsBuilder) + { + Diagnostics.Add(pair.DiagnosticInfo, syntax.Location); + } + useSiteDiagnosticsBuilder?.Free(); + warningsBuilder.Free(); + diagnosticsBuilder.Free(); + } + private void ReplayReadsAndWrites(LocalFunctionSymbol localFunc, SyntaxNode syntax, bool writes) diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index bfe7d45b8616c..38e943285e590 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -203,6 +203,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_NullAsNonNullable: case ErrorCode.WRN_NoBestNullabilityConditionalExpression: case ErrorCode.WRN_MissingNonNullTypesContext: + case ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 2bc415c511030..501ba01cc92d1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -702,7 +702,7 @@ public static bool CheckConstraints( var diagnosticsBuilder = ArrayBuilder.GetInstance(); ArrayBuilder useSiteDiagnosticsBuilder = null; - var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); + var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, warningsBuilderOpt: null, ref useSiteDiagnosticsBuilder); if (useSiteDiagnosticsBuilder != null) { @@ -733,7 +733,7 @@ public static bool CheckConstraints( var diagnosticsBuilder = ArrayBuilder.GetInstance(); ArrayBuilder useSiteDiagnosticsBuilder = null; - var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); + var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, warningsBuilderOpt: null, ref useSiteDiagnosticsBuilder); if (useSiteDiagnosticsBuilder != null) { @@ -764,6 +764,7 @@ private static bool CheckTypeConstraints( type.TypeArgumentsNoUseSiteDiagnostics, currentCompilation, diagnosticsBuilder, + warningsBuilderOpt: conversions.IncludeNullability ? diagnosticsBuilder : null, ref useSiteDiagnosticsBuilder); } @@ -772,6 +773,7 @@ public static bool CheckMethodConstraints( ConversionsBase conversions, Compilation currentCompilation, ArrayBuilder diagnosticsBuilder, + ArrayBuilder warningsBuilderOpt, ref ArrayBuilder useSiteDiagnosticsBuilder, BitVector skipParameters = default(BitVector)) { @@ -783,6 +785,7 @@ public static bool CheckMethodConstraints( method.TypeArguments, currentCompilation, diagnosticsBuilder, + warningsBuilderOpt, ref useSiteDiagnosticsBuilder, skipParameters); } @@ -797,6 +800,7 @@ public static bool CheckMethodConstraints( /// Containing symbol type arguments. /// Improves error message detail. /// Diagnostics. + /// Nullability warnings. /// Parameters to skip. /// /// If an original form of a type constraint @@ -810,12 +814,14 @@ public static bool CheckConstraints( ImmutableArray typeArguments, Compilation currentCompilation, ArrayBuilder diagnosticsBuilder, + ArrayBuilder warningsBuilderOpt, ref ArrayBuilder useSiteDiagnosticsBuilder, BitVector skipParameters = default(BitVector), HashSet ignoreTypeConstraintsDependentOnTypeParametersOpt = null) { Debug.Assert(typeParameters.Length == typeArguments.Length); Debug.Assert(typeParameters.Length > 0); + Debug.Assert(warningsBuilderOpt == null || conversions.IncludeNullability); int n = typeParameters.Length; bool succeeded = true; @@ -830,7 +836,7 @@ public static bool CheckConstraints( var typeArgument = typeArguments[i]; var typeParameter = typeParameters[i]; - if (!CheckConstraints(containingSymbol, conversions, substitution, typeParameter, typeArgument, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder, + if (!CheckConstraints(containingSymbol, conversions, substitution, typeParameter, typeArgument, currentCompilation, diagnosticsBuilder, warningsBuilderOpt, ref useSiteDiagnosticsBuilder, ignoreTypeConstraintsDependentOnTypeParametersOpt)) { succeeded = false; @@ -849,6 +855,7 @@ private static bool CheckConstraints( TypeSymbolWithAnnotations typeArgument, Compilation currentCompilation, ArrayBuilder diagnosticsBuilder, + ArrayBuilder warningsBuilderOpt, ref ArrayBuilder useSiteDiagnosticsBuilder, HashSet ignoreTypeConstraintsDependentOnTypeParametersOpt) { @@ -911,8 +918,17 @@ private static bool CheckConstraints( foreach (var constraintType in constraintTypes) { - if (SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics)) + if (SatisfiesConstraintType(conversions.WithNullability(false), typeArgument, constraintType, ref useSiteDiagnostics)) { + if (warningsBuilderOpt != null) + { + Debug.Assert(conversions.IncludeNullability); + if (!SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics)) + { + var diagnostic = new CSDiagnosticInfo(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, containingSymbol.ConstructedFrom(), constraintType, typeParameter, typeArgument); + warningsBuilderOpt.Add(new TypeParameterDiagnosticInfo(typeParameter, diagnostic)); + } + } continue; } @@ -1003,7 +1019,10 @@ private static bool SatisfiesConstraintType( // "An identity conversion (6.1.1). // An implicit reference conversion (6.1.6). ..." - if (conversions.HasIdentityOrImplicitReferenceConversion(typeArgument.TypeSymbol, constraintType.TypeSymbol, ref useSiteDiagnostics)) + + // When nullability is considered, top-level nullability must be implicitly convertible. + if ((!conversions.IncludeNullability || ConversionsBase.HasTopLevelNullabilityImplicitConversion(typeArgument, constraintType)) && + conversions.HasIdentityOrImplicitReferenceConversion(typeArgument.TypeSymbol, constraintType.TypeSymbol, ref useSiteDiagnostics)) { return true; } @@ -1126,6 +1145,9 @@ private static bool IsEncompassedBy(ConversionsBase conversions, TypeSymbol a, T Debug.Assert(IsValidEncompassedByArgument(a)); Debug.Assert(IsValidEncompassedByArgument(b)); + // IncludeNullability should not be used when calculating EffectiveBaseType or EffectiveInterfaceSet. + Debug.Assert(!conversions.IncludeNullability); + return conversions.HasIdentityOrImplicitReferenceConversion(a, b, ref useSiteDiagnostics) || conversions.HasBoxingConversion(a, b, ref useSiteDiagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index b118131ad8773..5fcdab7b5123d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -113,7 +113,7 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m var diagnosticsBuilder = ArrayBuilder.GetInstance(); var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck); ArrayBuilder useSiteDiagnosticsBuilder = null; - var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder, + var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, warningsBuilderOpt: null, ref useSiteDiagnosticsBuilder, ignoreTypeConstraintsDependentOnTypeParametersOpt: notInferredTypeParameters.Count > 0 ? notInferredTypeParameters : null); diagnosticsBuilder.Free(); notInferredTypeParameters.Free(); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 4390fde73555f..991b8899b1784 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -9050,6 +9050,16 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 1ef312d3c3914..161b60dfa4143 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -9050,6 +9050,16 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index aae0c0eccd198..aa8b8ae4ea7be 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -9050,6 +9050,16 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 42bfa54390d95..1fa0516736b2f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -9050,6 +9050,16 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index c527b224b0304..deb1b51596c5a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -9050,6 +9050,16 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index c79231f80710e..e93f9aaa013f9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -9050,6 +9050,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index c07abe991602d..952c8886c3cc6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -9050,6 +9050,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 87b2da98d948d..ff46f483c86ed 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -9050,6 +9050,16 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 1c17690acdbd5..686e4c767b2ff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -9050,6 +9050,16 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index a665f7ff915ec..25c9e1797ac65 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -9050,6 +9050,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 6d01e80b1a5e7..6323c77556dea 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -9050,6 +9050,16 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index f1f9201cd7918..3f0fb9b568ec7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -9050,6 +9050,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 0946800a81436..bc17a90d22278 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -9050,6 +9050,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index 9ae2cbd80da8c..f8424aece4c07 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -14,6 +14,18 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class AttributeTests_Nullable : CSharpTestBase { + private const string NonNullTypesAttributesDefinition = @" +namespace System.Runtime.CompilerServices +{ + [System.AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public sealed class NonNullTypesAttribute : Attribute + { + public NonNullTypesAttribute(bool flag = true) { } + } +} +"; + private const string NonNullTypesTrue = "[module: System.Runtime.CompilerServices.NonNullTypes(true)]"; + // An empty project should not require System.Attribute. [Fact] public void EmptyProject_MissingAttribute() @@ -312,18 +324,6 @@ public void EmitAttribute_NetModuleNoDeclarations() }); } - private const string NonNullTypesAttributesDefinition = @" -namespace System.Runtime.CompilerServices -{ - [System.AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public sealed class NonNullTypesAttribute : Attribute - { - public NonNullTypesAttribute(bool flag = true) { } - } -} -"; - private const string NonNullTypesTrue = "[module: System.Runtime.CompilerServices.NonNullTypes(true)]"; - [Fact] public void EmitAttribute_BaseClass() { @@ -487,7 +487,7 @@ public class C where T : A? public class D where T : A { }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); CompileAndVerify(comp, validator: assembly => { var reader = assembly.GetMetadataReader(); @@ -511,19 +511,29 @@ static void Main() new C(); new C(); new C(); - new D(); + new D(); // warning new D(); - new D(); + new D(); // warning new D(); } }"; - var comp2 = CreateCompilation(new[] { source, source2 }, parseOptions: TestOptions.Regular8); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new D()` and `new D()`: - comp2.VerifyEmitDiagnostics(); - - comp2 = CreateCompilation(source2, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new D()` and `new D()`: - comp2.VerifyEmitDiagnostics(); + var comp2 = CreateCompilation(new[] { source, source2, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); + comp2.VerifyEmitDiagnostics( + // (10,15): warning CS8627: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("D", "A", "T", "A?").WithLocation(10, 15), + // (12,15): warning CS8627: The type 'B?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'B?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B?").WithArguments("D", "A", "T", "B?").WithLocation(12, 15)); + + comp2 = CreateCompilation(new[] { source2, NonNullTypesTrue }, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); + comp2.VerifyEmitDiagnostics( + // (10,15): warning CS8627: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("D", "A", "T", "A?").WithLocation(10, 15), + // (12,15): warning CS8627: The type 'B?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'B?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B?").WithArguments("D", "A", "T", "B?").WithLocation(12, 15)); var type = comp2.GetMember("C"); Assert.Equal("A?", type.TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); @@ -590,7 +600,7 @@ public class B where T : A public class C where T : A { }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); CompileAndVerify(comp, validator: assembly => { var reader = assembly.GetMetadataReader(); @@ -615,13 +625,23 @@ static void Main() new C>(); } }"; - var comp2 = CreateCompilation(new[] { source, source2 }, parseOptions: TestOptions.Regular8); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new B>()` and `new C>()`. - comp2.VerifyEmitDiagnostics(); + var comp2 = CreateCompilation(new[] { source, source2, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); + comp2.VerifyEmitDiagnostics( + // (6,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new B>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B", "A", "T", "A").WithLocation(6, 15), + // (7,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'C'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new C>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("C", "A", "T", "A").WithLocation(7, 15)); - comp2 = CreateCompilation(source2, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new B>()` and `new C>()`. - comp2.VerifyDiagnostics(); + comp2 = CreateCompilation(new[] { source2, NonNullTypesTrue }, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics( + // (6,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new B>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B", "A", "T", "A").WithLocation(6, 15), + // (7,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'C'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new C>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("C", "A", "T", "A").WithLocation(7, 15)); var type = comp2.GetMember("B"); Assert.Equal("A!", type.TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 4fd2e02daf4ba..40b535c95bd11 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -16,6 +16,8 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class NullableReferenceTypesTests : CSharpTestBase { + // PROTOTYPE(NullableReferenceTypes): Move attribute definitions to base class. + private const string NullableAttributeDefinition = @" namespace System.Runtime.CompilerServices { @@ -4039,12 +4041,11 @@ void Dummy() } } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): syntax-based detection of NonNullTypes is temporary")] + [Fact] public void Overriding_Methods() { var source = @" using System.Runtime.CompilerServices; -[module: NonNullTypes(true)] public abstract class A { [NonNullTypes(false)] @@ -4081,11 +4082,17 @@ public class B2 : A var compilation = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); compilation.VerifyDiagnostics( + // (7,14): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [return: NonNullTypes(false)] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(7, 14), + // (8,55): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public abstract System.Action Oblivious2([NonNullTypes(false)] System.Action x); + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(8, 55), // (17,44): warning CS8609: Nullability of reference types in return type doesn't match overridden member. - // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 + // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 // PROTOTYPE(NullableReferenceTypes): Should not warn Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "Oblivious2").WithLocation(17, 44), // (17,44): warning CS8610: Nullability of reference types in type of parameter 'x' doesn't match overridden member. - // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 + // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 // PROTOTYPE(NullableReferenceTypes): Should not warn Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "Oblivious2").WithArguments("x").WithLocation(17, 44), // (18,44): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // public override System.Action M3(System.Action x) => throw null; // warn 5 and 6 @@ -4104,7 +4111,19 @@ public class B2 : A Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M5").WithLocation(20, 44), // (20,44): warning CS8610: Nullability of reference types in type of parameter 'x' doesn't match overridden member. // public override System.Action M5(System.Action x) => throw null; // warn 9 and 10 - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("x").WithLocation(20, 44) + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("x").WithLocation(20, 44), + // (30,14): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [return: NonNullTypes(false)] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(30, 14), + // (31,47): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override System.Action M4([NonNullTypes(false)] System.Action x) => throw null; + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(31, 47), + // (25,14): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [return: NonNullTypes(false)] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(25, 14), + // (26,55): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override System.Action Oblivious1([NonNullTypes(false)] System.Action x) => throw null; + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(26, 55) ); var b1 = compilation.GetTypeByMetadataName("B1"); @@ -4177,12 +4196,11 @@ public class Class : Base where T : class comp.VerifyDiagnostics(); } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): hits an assertion in AsObliviousReferenceType")] + [Fact] public void Overriding_Properties_WithNullableTypeArgument_WithStructConstraint() { var source = @" using System.Runtime.CompilerServices; -[module: NonNullTypes(true)] public class List { } public class Base where T : struct { @@ -4198,12 +4216,11 @@ public class Class : Base where T : struct comp.VerifyDiagnostics(); } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): hits an assertion in CopyTypeCustomModifiers")] + [Fact] public void Overriding_Indexer() { var source = @" using System.Runtime.CompilerServices; -[module: NonNullTypes(true)] public class List { } public class Base { @@ -4220,7 +4237,13 @@ public class Class2 : Base } "; var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (15,92): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override List this[[NonNullTypes(false)] List x] { [return: NonNullTypes(false)] get => throw null; set => throw null; } + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(15, 92), + // (15,42): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override List this[[NonNullTypes(false)] List x] { [return: NonNullTypes(false)] get => throw null; set => throw null; } + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(15, 42)); } [Fact] @@ -4583,7 +4606,7 @@ public override string?[]? this[long x] // 3 } } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): hits assertion in AsObliviousReferenceType")] + [Fact] public void Overriding_22() { var source = @@ -12045,7 +12068,7 @@ class CL1 ); } - [Fact(Skip = "Unexpected warning")] + [Fact] public void ConditionalBranching_08() { CSharpCompilation c = CreateCompilation(new[] { @" @@ -12072,8 +12095,12 @@ class CL1 public bool P2 { get { return true;} } } ", NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); - + // PROTOTYPE(NullableReferenceTypes): Not tracking state of x?.P == expr + // unless expr is `null`. See https://github.com/dotnet/roslyn/issues/26624. c.VerifyDiagnostics( + // (12,20): warning CS8602: Possible dereference of a null reference. + // return x1.P2; + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(12, 20) ); } @@ -33938,9 +33965,9 @@ public void TypeInference_06() static T F(T t, U u) where U : T => t; static void G(C? x, C y) { - F(x, x).ToString(); - F(x, y).ToString(); - F(y, x).ToString(); + F(x, x).ToString(); // warning: may be null + F(x, y).ToString(); // warning may be null + F(y, x).ToString(); // warning: x does not satisfy U constraint F(y, y).ToString(); } }"; @@ -33949,11 +33976,14 @@ static void G(C? x, C y) parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( // (6,9): warning CS8602: Possible dereference of a null reference. - // F(x, x).ToString(); + // F(x, x).ToString(); // warning: may be null Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, x)").WithLocation(6, 9), // (7,9): warning CS8602: Possible dereference of a null reference. - // F(x, y).ToString(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, y)").WithLocation(7, 9)); + // F(x, y).ToString(); // warning may be null + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, y)").WithLocation(7, 9), + // (8,9): warning CS8631: The type 'C?' cannot be used as type parameter 'U' in the generic type or method 'C.F(T, U)'. Nullability of type argument 'C?' doesn't match constraint type 'C'. + // F(y, x).ToString(); // warning: x does not satisfy U constraint + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F").WithArguments("C.F(T, U)", "C", "U", "C?").WithLocation(8, 9)); } [Fact] @@ -37514,7 +37544,10 @@ static void Main() } }"; var comp = CreateCompilation(new[] { source, NonNullTypesTrue }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (8,29): warning CS8631: The type 'A2' cannot be used as type parameter 'U' in the generic type or method 'B2'. Nullability of type argument 'A2' doesn't match constraint type 'A2'. + // new B2, A2>(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A2").WithArguments("B2", "A2", "U", "A2").WithLocation(8, 29)); var typeParameters = comp.GetMember("B1").TypeParameters; Assert.Equal("A1", typeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); Assert.Equal("A1?", typeParameters[1].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); @@ -37618,32 +37651,33 @@ public void Constraint_Oblivious_01() } public class A where T : I { - public static void F(T t) { } }"; var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); var ref0 = comp0.EmitToImageReference(); var source = -@"class B1 : I { } +@"using System; +class B1 : I { } class B2 : I { } class C { static void Main() { - A.F(null); // warning - A.F(null); // warning - A.F(null); - A.F(null); + Type t; + t = typeof(A); + t = typeof(A); // 1 + t = typeof(A); // 2 + t = typeof(A); } }"; var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8, references: new[] { ref0 }); comp.VerifyDiagnostics( - // (7,17): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. - // A.F(null); // warning - Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 17), - // (8,17): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. - // A.F(null); // warning - Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 17)); + // (10,22): warning CS8631: The type 'B2' cannot be used as type parameter 'T' in the generic type or method 'A'. Nullability of type argument 'B2' doesn't match constraint type 'I'. + // t = typeof(A); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B2").WithArguments("A", "I", "T", "B2").WithLocation(10, 22), + // (11,22): warning CS8631: The type 'B1?' cannot be used as type parameter 'T' in the generic type or method 'A'. Nullability of type argument 'B1?' doesn't match constraint type 'I'. + // t = typeof(A); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B1?").WithArguments("A", "I", "T", "B1?").WithLocation(11, 22)); var constraintTypes = comp.GetMember("A").TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics; Assert.Equal("I", constraintTypes[0].ToTestDisplayString(true)); @@ -37673,6 +37707,355 @@ static void F(I i) comp.VerifyDiagnostics(); } + [Fact] + public void Constraint_Oblivious_03() + { + var source0 = +@"public class A { } +public class B0 where T : A { }"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"#pragma warning disable 0169 +#pragma warning disable 8618 +using System.Runtime.CompilerServices; +class B1 where T : A? { } +class B2 where T : A { } +[NonNullTypes] class B3 where T : A? { } +[NonNullTypes] class B4 where T : A { } +class C +{ + B0 F1; + B0 F2; + B1 F3; + B1 F4; + B2 F5; + B2 F6; + B3 F7; + B3 F8; + B4 F9; // 1 + B4 F10; +} +[NonNullTypes] +class D +{ + B0 G1; + B0 G2; + B1 G3; + B1 G4; + B2 G5; + B2 G6; + B3 G7; + B3 G8; + B4 G9; // 2 + B4 G10; +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (18,8): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // B4 F9; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("B4", "A", "T", "A?").WithLocation(18, 8), + // (32,8): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // B4 G9; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("B4", "A", "T", "A?").WithLocation(32, 8)); + } + + [Fact] + public void Constraint_Oblivious_04() + { + var source0 = +@"public class A { } +public class B0 where T : A { }"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"#pragma warning disable 0169 +#pragma warning disable 8618 +using System.Runtime.CompilerServices; +class B1 where T : A { } +class B2 where T : A { } +[NonNullTypes] class B3 where T : A { } +[NonNullTypes] class B4 where T : A { } +class C +{ + B0> F1; + B0> F2; + B1> F3; + B1> F4; + B2> F5; + B2> F6; + B3> F7; + B3> F8; + B4> F9; // 1 + B4> F10; +} +[NonNullTypes] +class D +{ + B0> G1; + B0> G2; + B1> G3; + B1> G4; // 2 + B2> G5; + B2> G6; + B3> G7; + B3> G8; // 3 + B4> G9; // 4 + B4> G10; +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (18,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B4> F9; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B4", "A", "T", "A").WithLocation(18, 8), + // (27,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B1'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B1> G4; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B1", "A", "T", "A").WithLocation(27, 8), + // (31,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B3'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B3> G8; // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B3", "A", "T", "A").WithLocation(31, 8), + // (32,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B4> G9; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B4", "A", "T", "A").WithLocation(32, 8)); + } + + [Fact] + public void Constraint_TypeParameterConstraint() + { + var source0 = +@"public class A1 + where T : class + where U : class, T +{ +} +public class A2 + where T : class + where U : class, T? +{ +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular8); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"using System.Runtime.CompilerServices; +class B1 where T : A1 { } +class B2 where T : A2 { } +[NonNullTypes] class B3 where T : A1 { } +[NonNullTypes] class B4 where T : A2 { }"; + var comp = CreateCompilation(new[] { source, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + // PROTOTYPE(NullableReferenceTypes): Should report warnings for each. + comp.VerifyDiagnostics(); + } + + // Boxing conversion. + [Fact] + public void Constraint_BoxingConversion() + { + var source0 = +@"public interface I { } +public interface IIn { } +public interface IOut { } +public struct S0 : I { } +public struct SIn0 : IIn { } +public struct SOut0 : IOut { } +public class A +{ + public static void F0() where T : I { } + public static void FIn0() where T : IIn { } + public static void FOut0() where T : IOut { } +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"struct S1 : I { } +struct S2 : I { } +struct SIn1 : IIn { } +struct SIn2 : IIn { } +struct SOut1 : IOut { } +struct SOut2 : IOut { } +class B : A +{ + static void F1() where T : I { } + static void F2() where T : I { } + static void FIn1() where T : IIn { } + static void FIn2() where T : IIn { } + static void FOut1() where T : IOut { } + static void FOut2() where T : IOut { } + static void F() + { + F0(); + F0(); + F0(); + F1(); + F1(); + F1(); // 1 + F2(); + F2(); // 2 + F2(); + } + static void FIn() + { + FIn0(); + FIn0(); + FIn0(); + FIn1(); + FIn1(); + FIn1(); // 3 + FIn2(); + FIn2(); + FIn2(); + } + static void FOut() + { + FOut0(); + FOut0(); + FOut0(); + FOut1(); + FOut1(); + FOut1(); + FOut2(); + FOut2(); // 4 + FOut2(); + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (22,9): warning CS8627: The type 'S2' cannot be used as type parameter 'T' in the generic type or method 'B.F1()'. Nullability of type argument 'S2' doesn't match constraint type 'I'. + // F1(); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("B.F1()", "I", "T", "S2").WithLocation(22, 9), + // (24,9): warning CS8627: The type 'S1' cannot be used as type parameter 'T' in the generic type or method 'B.F2()'. Nullability of type argument 'S1' doesn't match constraint type 'I'. + // F2(); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F2").WithArguments("B.F2()", "I", "T", "S1").WithLocation(24, 9), + // (34,9): warning CS8627: The type 'SIn2' cannot be used as type parameter 'T' in the generic type or method 'B.FIn1()'. Nullability of type argument 'SIn2' doesn't match constraint type 'IIn'. + // FIn1(); // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FIn1").WithArguments("B.FIn1()", "IIn", "T", "SIn2").WithLocation(34, 9), + // (48,9): warning CS8627: The type 'SOut1' cannot be used as type parameter 'T' in the generic type or method 'B.FOut2()'. Nullability of type argument 'SOut1' doesn't match constraint type 'IOut'. + // FOut2(); // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FOut2").WithArguments("B.FOut2()", "IOut", "T", "SOut1").WithLocation(48, 9)); + } + + [Fact] + public void Constraint_ImplicitTypeParameterConversion() + { + var source0 = +@"public interface I { } +public interface IIn { } +public interface IOut { } +public class A +{ + public static void F0() where T : I { } + public static void FIn0() where T : IIn { } + public static void FOut0() where T : IOut { } +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"class B : A +{ + static void F1() where T : I { } + static void F2() where T : I { } + static void FIn1() where T : IIn { } + static void FIn2() where T : IIn { } + static void FOut1() where T : IOut { } + static void FOut2() where T : IOut { } + static void F() where T : I where U : I + { + F0(); + F0(); + F1(); + F1(); // 1 + F2(); // 2 + F2(); + } + static void FIn() where T : IIn where U : IIn + { + FIn0(); + FIn0(); + FIn1(); + FIn1(); // 3 + FIn2(); + FIn2(); + } + static void FOut() where T : IOut where U : IOut + { + FOut0(); + FOut0(); + FOut1(); + FOut1(); + FOut2(); // 4 + FOut2(); + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (14,9): warning CS8627: The type 'U' cannot be used as type parameter 'T' in the generic type or method 'B.F1()'. Nullability of type argument 'U' doesn't match constraint type 'I'. + // F1(); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("B.F1()", "I", "T", "U").WithLocation(14, 9), + // (15,9): warning CS8627: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'B.F2()'. Nullability of type argument 'T' doesn't match constraint type 'I'. + // F2(); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F2").WithArguments("B.F2()", "I", "T", "T").WithLocation(15, 9), + // (23,9): warning CS8627: The type 'U' cannot be used as type parameter 'T' in the generic type or method 'B.FIn1()'. Nullability of type argument 'U' doesn't match constraint type 'IIn'. + // FIn1(); // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FIn1").WithArguments("B.FIn1()", "IIn", "T", "U").WithLocation(23, 9), + // (33,9): warning CS8627: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'B.FOut2()'. Nullability of type argument 'T' doesn't match constraint type 'IOut'. + // FOut2(); // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FOut2").WithArguments("B.FOut2()", "IOut", "T", "T").WithLocation(33, 9)); + } + + [Fact] + public void Constraint_MethodTypeInference() + { + var source0 = +@"public class A { } +public class B +{ + public static void F0(T t) where T : A + { + } +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"class C : B +{ + static void F1(T t) where T : A + { + } + static void G(A x, A? y) + { + F0(x); + F1(x); + F0(y); + F1(y); // 1 + x = y; + F0(x); + F1(x); // 2 + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (11,9): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'C.F1(T)'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // F1(y); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("C.F1(T)", "A", "T", "A?").WithLocation(11, 9), + // (12,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = y; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "y").WithLocation(12, 13), + // (14,9): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'C.F1(T)'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // F1(x); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("C.F1(T)", "A", "T", "A?").WithLocation(14, 9)); + } + [Fact] public void ThisAndBaseMemberInLambda() { diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 224a430b7fff2..24487e7f15ed8 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -280,6 +280,7 @@ public void WarningLevel_2() case ErrorCode.WRN_NullAsNonNullable: case ErrorCode.WRN_NoBestNullabilityConditionalExpression: case ErrorCode.WRN_MissingNonNullTypesContext: + case ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_InvalidVersionFormat: