File: Symbols\FunctionPointerTypeSymbolTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Symbol\Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using Microsoft.Cci;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class FunctionPointerTypeSymbolTests : CSharpTestBase
    {
        private static CSharpCompilation CreateFunctionPointerCompilation(string source, TargetFramework targetFramework = TargetFramework.Standard)
        {
            return CreateCompilation(source, parseOptions: TestOptions.Regular9, options: TestOptions.UnsafeReleaseDll, targetFramework: targetFramework);
        }
 
        [InlineData("", RefKind.None, "delegate*<System.Object>")]
        [InlineData("ref", RefKind.Ref, "delegate*<ref System.Object>")]
        [InlineData("ref readonly", RefKind.RefReadOnly,
                    "delegate*<ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.Object>")]
        [Theory]
        public void ValidReturnModifiers(string modifier, RefKind expectedKind, string expectedPublicType)
        {
            var comp = CreateFunctionPointerCompilation($@"
class C
{{
    unsafe void M(delegate*<{modifier} object> p) {{}}
 
}}");
            comp.VerifyDiagnostics();
 
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            var pointerType = (FunctionPointerTypeSymbol)m.Parameters.Single().Type;
            FunctionPointerUtilities.CommonVerifyFunctionPointer(pointerType);
            Assert.Equal(expectedKind, pointerType.Signature.RefKind);
            Assert.Equal(SpecialType.System_Object, pointerType.Signature.ReturnType.SpecialType);
            Assert.Empty(pointerType.Signature.Parameters);
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var paramType = syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<MethodDeclarationSyntax>()
                .Single().ParameterList.Parameters
                .Single().Type;
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, paramType!,
                expectedSyntax: $"delegate*<{modifier} object>",
                expectedType: expectedPublicType,
                expectedSymbol: expectedPublicType);
        }
 
        [Fact]
        public void InvalidReturnModifiers()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(
        delegate*<readonly string> p1,
        delegate*<readonly ref string> p2,
        delegate*<ref ref readonly string> p3,
        delegate*<ref readonly readonly string> p4,
        delegate*<this string> p5,
        delegate*<params string> p6,
        delegate*<ref ref string> p7,
        delegate*<out string> p8)
    {}
}
");
            comp.VerifyDiagnostics(
                    // (5,19): error CS8808: 'readonly' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                    //         delegate*<readonly string> p1,
                    Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "readonly").WithArguments("readonly").WithLocation(5, 19),
                    // (6,19): error CS8808: 'readonly' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                    //         delegate*<readonly ref string> p2,
                    Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "readonly").WithArguments("readonly").WithLocation(6, 19),
                    // (7,23): error CS8809: A return type can only have one 'ref' modifier.
                    //         delegate*<ref ref readonly string> p3,
                    Diagnostic(ErrorCode.ERR_DupReturnTypeMod, "ref").WithArguments("ref").WithLocation(7, 23),
                    // (7,27): error CS8808: 'readonly' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                    //         delegate*<ref ref readonly string> p3,
                    Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "readonly").WithArguments("readonly").WithLocation(7, 27),
                    // (8,32): error CS8808: 'readonly' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                    //         delegate*<ref readonly readonly string> p4,
                    Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "readonly").WithArguments("readonly").WithLocation(8, 32),
                    // (9,19): error CS8808: 'this' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                    //         delegate*<this string> p5,
                    Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "this").WithArguments("this").WithLocation(9, 19),
                    // (10,19): error CS8808: 'params' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                    //         delegate*<params string> p6,
                    Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "params").WithArguments("params").WithLocation(10, 19),
                    // (11,23): error CS8809: A return type can only have one 'ref' modifier.
                    //         delegate*<ref ref string> p7)
                    Diagnostic(ErrorCode.ERR_DupReturnTypeMod, "ref").WithArguments("ref").WithLocation(11, 23),
                    // (12,19): error CS8808: 'out' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                    //         delegate*<out string> p8)
                    Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "out").WithArguments("out").WithLocation(12, 19));
 
            var mParams = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters;
            Assert.Equal(8, mParams.Length);
 
            verifyRefKind(RefKind.None, mParams[0]);
            verifyRefKind(RefKind.Ref, mParams[1]);
            verifyRefKind(RefKind.Ref, mParams[2]);
            verifyRefKind(RefKind.RefReadOnly, mParams[3]);
            verifyRefKind(RefKind.None, mParams[4]);
            verifyRefKind(RefKind.None, mParams[5]);
            verifyRefKind(RefKind.Ref, mParams[6]);
            verifyRefKind(RefKind.None, mParams[7]);
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var parameterDecls = syntaxTree.GetRoot().DescendantNodes()
                .OfType<MethodDeclarationSyntax>()
                .SelectMany(m => m.ParameterList.Parameters)
                .Select(p => p.Type!)
                .ToArray();
 
            Assert.Equal(8, parameterDecls.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[0],
                expectedSyntax: "delegate*<readonly string>",
                expectedType: "delegate*<System.String>",
                expectedSymbol: "delegate*<System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[1],
                expectedSyntax: "delegate*<readonly ref string>",
                expectedType: "delegate*<ref System.String>",
                expectedSymbol: "delegate*<ref System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[2],
                expectedSyntax: "delegate*<ref ref readonly string>",
                expectedType: "delegate*<ref System.String>",
                expectedSymbol: "delegate*<ref System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[3],
                expectedSyntax: "delegate*<ref readonly readonly string>",
                expectedType: "delegate*<ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.String>",
                expectedSymbol: "delegate*<ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[4],
                expectedSyntax: "delegate*<this string>",
                expectedType: "delegate*<System.String>",
                expectedSymbol: "delegate*<System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[5],
                expectedSyntax: "delegate*<params string>",
                expectedType: "delegate*<System.String>",
                expectedSymbol: "delegate*<System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[6],
                expectedSyntax: "delegate*<ref ref string>",
                expectedType: "delegate*<ref System.String>",
                expectedSymbol: "delegate*<ref System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[7],
                expectedSyntax: "delegate*<out string>",
                expectedType: "delegate*<System.String>",
                expectedSymbol: "delegate*<System.String>");
 
            static void verifyRefKind(RefKind expected, ParameterSymbol actual)
                => Assert.Equal(expected, ((FunctionPointerTypeSymbol)actual.Type).Signature.RefKind);
        }
 
        [Fact]
        public void InvalidModifiersOnVoidReturnType()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(
        delegate*<ref void> p1,
        delegate*<ref readonly void> p2) {}
}");
            comp.VerifyDiagnostics(
                    // (5,19): error CS1547: Keyword 'void' cannot be used in this context
                    //         delegate*<ref void> p1,
                    Diagnostic(ErrorCode.ERR_NoVoidHere, "ref void").WithLocation(5, 19),
                    // (6,19): error CS1547: Keyword 'void' cannot be used in this context
                    //         delegate*<ref readonly void> p2) {}
                    Diagnostic(ErrorCode.ERR_NoVoidHere, "ref readonly void").WithLocation(6, 19));
 
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            var firstSignature = ((FunctionPointerTypeSymbol)m.Parameters[0].Type).Signature;
            var secondSignature = ((FunctionPointerTypeSymbol)m.Parameters[1].Type).Signature;
 
            Assert.Equal(RefKind.Ref, firstSignature.RefKind);
            Assert.Equal(RefKind.RefReadOnly, secondSignature.RefKind);
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var parameterDecls = syntaxTree.GetRoot().DescendantNodes()
                .OfType<MethodDeclarationSyntax>()
                .SelectMany(m => m.ParameterList.Parameters)
                .Select(p => p.Type!)
                .ToArray();
 
            Assert.Equal(2, parameterDecls.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[0],
                expectedSyntax: "delegate*<ref void>",
                expectedType: "delegate*<ref System.Void>",
                expectedSymbol: "delegate*<ref System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[1],
                expectedSyntax: "delegate*<ref readonly void>",
                expectedType: "delegate*<ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.Void>",
                expectedSymbol: "delegate*<ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.Void>");
        }
 
        [InlineData("", CallingConvention.Default)]
        [InlineData("managed", CallingConvention.Default)]
        [InlineData("unmanaged[Cdecl]", CallingConvention.CDecl)]
        [InlineData("unmanaged[Stdcall]", CallingConvention.Standard)]
        [InlineData("unmanaged[Thiscall]", CallingConvention.ThisCall)]
        [InlineData("unmanaged[Fastcall]", CallingConvention.FastCall)]
        [InlineData("unmanaged", CallingConvention.Unmanaged)]
        [Theory]
        internal void ValidCallingConventions(string convention, CallingConvention expectedConvention)
        {
            string source = $@"
class C
{{
    public unsafe void M(delegate* {convention}<string> p) {{}}
}}";
 
            verify(CreateFunctionPointerCompilation(source));
 
            var compWithMissingMembers = CreateFunctionPointerCompilation(source, targetFramework: TargetFramework.Minimal);
            Assert.Null(compWithMissingMembers.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"));
            Assert.Null(compWithMissingMembers.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvThiscall"));
            Assert.Null(compWithMissingMembers.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvFastcall"));
            Assert.Null(compWithMissingMembers.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall"));
            verify(compWithMissingMembers);
 
            void verify(CSharpCompilation comp)
            {
                if (expectedConvention == CallingConvention.Unmanaged)
                {
                    comp.VerifyDiagnostics(
                        // (4,36): error CS8889: The target runtime doesn't support extensible or runtime-environment default calling conventions.
                        //     public unsafe void M(delegate* unmanaged<string> p) {}
                        Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportUnmanagedDefaultCallConv, "unmanaged").WithLocation(4, 36)
                    );
                }
                else
                {
                    comp.VerifyDiagnostics();
                }
                var c = comp.GetTypeByMetadataName("C");
                var m = c.GetMethod("M");
                var pointerType = (FunctionPointerTypeSymbol)m.Parameters.Single().Type;
                FunctionPointerUtilities.CommonVerifyFunctionPointer(pointerType);
                Assert.Equal(expectedConvention, pointerType.Signature.CallingConvention);
                Assert.Equal(SpecialType.System_String, pointerType.Signature.ReturnType.SpecialType);
 
                var syntaxTree = comp.SyntaxTrees[0];
                var model = comp.GetSemanticModel(syntaxTree);
 
                string expectedType;
                switch (convention)
                {
                    case "":
                    case "managed":
                        expectedType = "delegate*<System.String>";
                        break;
                    default:
                        expectedType = $"delegate* {convention}<System.String>";
                        break;
                }
 
                FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model,
                    syntaxTree.GetRoot().DescendantNodes().OfType<FunctionPointerTypeSyntax>().Single(),
                    expectedSyntax: $"delegate* {convention}<string>",
                    expectedType: expectedType,
                    expectedSymbol: expectedType);
            }
        }
 
        [Fact]
        public void InvalidCallingConventions()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    public unsafe void M1(delegate* unmanaged[invalid]<void> p) {}
    public unsafe void M2(delegate* unmanaged[invalid, Stdcall]<void> p) {}
    public unsafe void M3(delegate* unmanaged[]<void> p) {}
}");
 
            comp.VerifyDiagnostics(
                // (4,47): error CS8889: The target runtime doesn't support extensible or runtime-environment default calling conventions.
                //     public unsafe void M1(delegate* unmanaged[invalid]<void> p) {}
                Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportUnmanagedDefaultCallConv, "invalid").WithLocation(4, 47),
                // (4,47): error CS8890: Type 'CallConvinvalid' is not defined.
                //     public unsafe void M1(delegate* unmanaged[invalid]<void> p) {}
                Diagnostic(ErrorCode.ERR_TypeNotFound, "invalid").WithArguments("CallConvinvalid").WithLocation(4, 47),
                // (5,37): error CS8889: The target runtime doesn't support extensible or runtime-environment default calling conventions.
                //     public unsafe void M2(delegate* unmanaged[invalid, Stdcall]<void> p) {}
                Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportUnmanagedDefaultCallConv, "unmanaged").WithLocation(5, 37),
                // (5,47): error CS8890: Type 'CallConvinvalid' is not defined.
                //     public unsafe void M2(delegate* unmanaged[invalid, Stdcall]<void> p) {}
                Diagnostic(ErrorCode.ERR_TypeNotFound, "invalid").WithArguments("CallConvinvalid").WithLocation(5, 47),
                // (6,47): error CS1001: Identifier expected
                //     public unsafe void M3(delegate* unmanaged[]<void> p) {}
                Diagnostic(ErrorCode.ERR_IdentifierExpected, "]").WithLocation(6, 47),
                // (6,47): error CS8889: The target runtime doesn't support extensible or runtime-environment default calling conventions.
                //     public unsafe void M3(delegate* unmanaged[]<void> p) {}
                Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportUnmanagedDefaultCallConv, "").WithLocation(6, 47)
            );
 
            var c = comp.GetTypeByMetadataName("C");
            var m1 = c.GetMethod("M1");
            var m1PointerType = (FunctionPointerTypeSymbol)m1.Parameters.Single().Type;
            Assert.Equal(CallingConvention.Unmanaged, m1PointerType.Signature.CallingConvention);
 
            var m2 = c.GetMethod("M2");
            var m2PointerType = (FunctionPointerTypeSymbol)m2.Parameters.Single().Type;
            Assert.Equal(CallingConvention.Unmanaged, m2PointerType.Signature.CallingConvention);
 
            var m3 = c.GetMethod("M3");
            var m3PointerType = (FunctionPointerTypeSymbol)m3.Parameters.Single().Type;
            Assert.Equal(CallingConvention.Unmanaged, m3PointerType.Signature.CallingConvention);
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var functionPointers = syntaxTree.GetRoot().DescendantNodes().OfType<FunctionPointerTypeSyntax>().ToArray();
            Assert.Equal(3, functionPointers.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, functionPointers[0],
                expectedSyntax: "delegate* unmanaged[invalid]<void>",
                expectedType: "delegate* unmanaged[invalid]<System.Void modopt(System.Runtime.CompilerServices.CallConvinvalid[missing])>",
                expectedSymbol: "delegate* unmanaged[invalid]<System.Void modopt(System.Runtime.CompilerServices.CallConvinvalid[missing])>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, functionPointers[1],
                expectedSyntax: "delegate* unmanaged[invalid, Stdcall]<void>",
                expectedType: "delegate* unmanaged[invalid, Stdcall]<System.Void modopt(System.Runtime.CompilerServices.CallConvinvalid[missing]) modopt(System.Runtime.CompilerServices.CallConvStdcall)>",
                expectedSymbol: "delegate* unmanaged[invalid, Stdcall]<System.Void modopt(System.Runtime.CompilerServices.CallConvinvalid[missing]) modopt(System.Runtime.CompilerServices.CallConvStdcall)>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, functionPointers[2],
                expectedSyntax: "delegate* unmanaged[]<void>",
                expectedType: "delegate* unmanaged<System.Void>",
                expectedSymbol: "delegate* unmanaged<System.Void>");
        }
 
        [Fact]
        public void Parameters()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    public unsafe void M<T>(
        delegate*<int, void> p1,
        delegate*<object, void> p2,
        delegate*<C, void> p3,
        delegate*<object, object, void> p4,
        delegate*<T, object, void> p5,
        delegate*<delegate*<T>, void> p6) {}
}");
            comp.VerifyDiagnostics();
 
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            var t = m.TypeParameters.Single();
            var parameterTypes = m.Parameters;
 
            var firstParam = getParam(0);
            Assert.Equal(SpecialType.System_Int32, firstParam.Parameters.Single().Type.SpecialType);
 
            var secondParam = getParam(1);
            Assert.Equal(SpecialType.System_Object, secondParam.Parameters.Single().Type.SpecialType);
 
            var thirdParam = getParam(2);
            Assert.Equal(c, thirdParam.Parameters.Single().Type);
 
            var fourthParam = getParam(3);
            Assert.Equal(2, fourthParam.ParameterCount);
            Assert.Equal(SpecialType.System_Object, fourthParam.Parameters[0].Type.SpecialType);
            Assert.Equal(SpecialType.System_Object, fourthParam.Parameters[1].Type.SpecialType);
 
            var fifthParam = getParam(4);
            Assert.Equal(2, fifthParam.ParameterCount);
            Assert.Equal(t, fifthParam.Parameters[0].Type);
            Assert.Equal(SpecialType.System_Object, fifthParam.Parameters[1].Type.SpecialType);
 
            var sixthParam = getParam(5);
            var sixthParamParam = ((FunctionPointerTypeSymbol)sixthParam.Parameters.Single().Type).Signature;
            Assert.Equal(t, sixthParamParam.ReturnType);
            Assert.Empty(sixthParamParam.Parameters);
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var parameterDecls = syntaxTree.GetRoot().DescendantNodes()
                .OfType<MethodDeclarationSyntax>()
                .SelectMany(m => m.ParameterList.Parameters)
                .Select(p => p.Type!)
                .ToArray();
 
            Assert.Equal(6, parameterDecls.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[0],
                expectedSyntax: "delegate*<int, void>",
                expectedType: "delegate*<System.Int32, System.Void>",
                expectedSymbol: "delegate*<System.Int32, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[1],
                expectedSyntax: "delegate*<object, void>",
                expectedType: "delegate*<System.Object, System.Void>",
                expectedSymbol: "delegate*<System.Object, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[2],
                expectedSyntax: "delegate*<C, void>",
                expectedType: "delegate*<C, System.Void>",
                expectedSymbol: "delegate*<C, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[3],
                expectedSyntax: "delegate*<object, object, void>",
                expectedType: "delegate*<System.Object, System.Object, System.Void>",
                expectedSymbol: "delegate*<System.Object, System.Object, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[4],
                expectedSyntax: "delegate*<T, object, void>",
                expectedType: "delegate*<T, System.Object, System.Void>",
                expectedSymbol: "delegate*<T, System.Object, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[5],
                expectedSyntax: "delegate*<delegate*<T>, void>",
                expectedType: "delegate*<delegate*<T>, System.Void>",
                expectedSymbol: "delegate*<delegate*<T>, System.Void>");
 
            MethodSymbol getParam(int index)
            {
                var type = ((FunctionPointerTypeSymbol)parameterTypes[index].Type);
                FunctionPointerUtilities.CommonVerifyFunctionPointer(type);
                Assert.True(type.Signature.ReturnsVoid);
                return type.Signature;
            }
        }
 
        [Fact]
        public void ValidParameterModifiers()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    public unsafe void M(
        delegate*<ref string, void> p1,
        delegate*<in string, void> p2,
        delegate*<out string, void> p3,
        delegate*<string, void> p4) {}
}");
 
            comp.VerifyDiagnostics();
 
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            var parameterTypes = m.Parameters;
 
            var firstParam = getParam(0);
            Assert.Equal(RefKind.Ref, firstParam.Parameters.Single().RefKind);
 
            var secondParam = getParam(1);
            Assert.Equal(RefKind.In, secondParam.Parameters.Single().RefKind);
 
            var thirdParam = getParam(2);
            Assert.Equal(RefKind.Out, thirdParam.Parameters.Single().RefKind);
 
            var fourthParam = getParam(3);
            Assert.Equal(RefKind.None, fourthParam.Parameters.Single().RefKind);
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var parameterDecls = syntaxTree.GetRoot().DescendantNodes()
                .OfType<MethodDeclarationSyntax>()
                .SelectMany(m => m.ParameterList.Parameters)
                .Select(p => p.Type!)
                .ToArray();
 
            Assert.Equal(4, parameterDecls.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[0],
                expectedSyntax: "delegate*<ref string, void>",
                expectedType: "delegate*<ref System.String, System.Void>",
                expectedSymbol: "delegate*<ref System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[1],
                expectedSyntax: "delegate*<in string, void>",
                expectedType: "delegate*<in modreq(System.Runtime.InteropServices.InAttribute) System.String, System.Void>",
                expectedSymbol: "delegate*<in modreq(System.Runtime.InteropServices.InAttribute) System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[2],
                expectedSyntax: "delegate*<out string, void>",
                expectedType: "delegate*<out modreq(System.Runtime.InteropServices.OutAttribute) System.String, System.Void>",
                expectedSymbol: "delegate*<out modreq(System.Runtime.InteropServices.OutAttribute) System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[3],
                expectedSyntax: "delegate*<string, void>",
                expectedType: "delegate*<System.String, System.Void>",
                expectedSymbol: "delegate*<System.String, System.Void>");
 
            MethodSymbol getParam(int index)
            {
                var type = ((FunctionPointerTypeSymbol)parameterTypes[index].Type);
                FunctionPointerUtilities.CommonVerifyFunctionPointer(type);
                return type.Signature;
            }
        }
 
        [Fact]
        public void InvalidParameterModifiers()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    public unsafe void M(
        delegate*<params string[], void> p1,
        delegate*<this string, void> p2,
        delegate*<readonly ref string, void> p3,
        delegate*<in out string, void> p4,
        delegate*<out in string, void> p5,
        delegate*<in ref string, void> p6,
        delegate*<ref in string, void> p7,
        delegate*<out ref string, void> p8,
        delegate*<ref out string, void> p9) {}
        
}");
 
            comp.VerifyDiagnostics(
                // (5,19): error CS8755: 'params' cannot be used as a modifier on a function pointer parameter.
                //         delegate*<params string[], void> p1,
                Diagnostic(ErrorCode.ERR_BadFuncPointerParamModifier, "params").WithArguments("params").WithLocation(5, 19),
                // (6,19): error CS0027: Keyword 'this' is not available in the current context
                //         delegate*<this string, void> p2,
                Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(6, 19),
                // (7,19): error CS9190: 'readonly' modifier must be specified after 'ref'.
                //         delegate*<readonly ref string, void> p3,
                Diagnostic(ErrorCode.ERR_RefReadOnlyWrongOrdering, "readonly").WithLocation(7, 19),
                // (8,22): error CS8328:  The parameter modifier 'out' cannot be used with 'in'
                //         delegate*<in out string, void> p4,
                Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "in").WithLocation(8, 22),
                // (9,23): error CS8328:  The parameter modifier 'in' cannot be used with 'out'
                //         delegate*<out in string, void> p5,
                Diagnostic(ErrorCode.ERR_BadParameterModifiers, "in").WithArguments("in", "out").WithLocation(9, 23),
                // (10,22): error CS8328:  The parameter modifier 'ref' cannot be used with 'in'
                //         delegate*<in ref string, void> p6,
                Diagnostic(ErrorCode.ERR_BadParameterModifiers, "ref").WithArguments("ref", "in").WithLocation(10, 22),
                // (11,23): error CS8328:  The parameter modifier 'in' cannot be used with 'ref'
                //         delegate*<ref in string, void> p7,
                Diagnostic(ErrorCode.ERR_BadParameterModifiers, "in").WithArguments("in", "ref").WithLocation(11, 23),
                // (12,23): error CS8328:  The parameter modifier 'ref' cannot be used with 'out'
                //         delegate*<out ref string, void> p8,
                Diagnostic(ErrorCode.ERR_BadParameterModifiers, "ref").WithArguments("ref", "out").WithLocation(12, 23),
                // (13,23): error CS8328:  The parameter modifier 'out' cannot be used with 'ref'
                //         delegate*<ref out string, void> p9) {}
                Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "ref").WithLocation(13, 23));
 
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            var parameterTypes = m.Parameters;
 
            var firstParam = getParam(0);
            Assert.Equal(RefKind.None, firstParam.Parameters.Single().RefKind);
 
            var secondParam = getParam(1);
            Assert.Equal(RefKind.None, secondParam.Parameters.Single().RefKind);
 
            var thirdParam = getParam(2);
            Assert.Equal(RefKind.Ref, thirdParam.Parameters.Single().RefKind);
 
            var fourthParam = getParam(3);
            Assert.Equal(RefKind.In, fourthParam.Parameters.Single().RefKind);
 
            var fifthParam = getParam(4);
            Assert.Equal(RefKind.Out, fifthParam.Parameters.Single().RefKind);
 
            var sixthParam = getParam(5);
            Assert.Equal(RefKind.In, sixthParam.Parameters.Single().RefKind);
 
            var seventhParam = getParam(6);
            Assert.Equal(RefKind.Ref, seventhParam.Parameters.Single().RefKind);
 
            var eightParam = getParam(7);
            Assert.Equal(RefKind.Out, eightParam.Parameters.Single().RefKind);
 
            var ninthParam = getParam(8);
            Assert.Equal(RefKind.Ref, ninthParam.Parameters.Single().RefKind);
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var parameterDecls = syntaxTree.GetRoot().DescendantNodes()
                .OfType<MethodDeclarationSyntax>()
                .SelectMany(m => m.ParameterList.Parameters)
                .Select(p => p.Type!)
                .ToArray();
 
            Assert.Equal(9, parameterDecls.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[0],
                expectedSyntax: "delegate*<params string[], void>",
                expectedType: "delegate*<System.String[], System.Void>",
                expectedSymbol: "delegate*<System.String[], System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[1],
                expectedSyntax: "delegate*<this string, void>",
                expectedType: "delegate*<System.String, System.Void>",
                expectedSymbol: "delegate*<System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[2],
                expectedSyntax: "delegate*<readonly ref string, void>",
                expectedType: "delegate*<ref System.String, System.Void>",
                expectedSymbol: "delegate*<ref System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[3],
                expectedSyntax: "delegate*<in out string, void>",
                expectedType: "delegate*<in modreq(System.Runtime.InteropServices.InAttribute) System.String, System.Void>",
                expectedSymbol: "delegate*<in modreq(System.Runtime.InteropServices.InAttribute) System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[4],
                expectedSyntax: "delegate*<out in string, void>",
                expectedType: "delegate*<out modreq(System.Runtime.InteropServices.OutAttribute) System.String, System.Void>",
                expectedSymbol: "delegate*<out modreq(System.Runtime.InteropServices.OutAttribute) System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[5],
                expectedSyntax: "delegate*<in ref string, void>",
                expectedType: "delegate*<in modreq(System.Runtime.InteropServices.InAttribute) System.String, System.Void>",
                expectedSymbol: "delegate*<in modreq(System.Runtime.InteropServices.InAttribute) System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[6],
                expectedSyntax: "delegate*<ref in string, void>",
                expectedType: "delegate*<ref System.String, System.Void>",
                expectedSymbol: "delegate*<ref System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[7],
                expectedSyntax: "delegate*<out ref string, void>",
                expectedType: "delegate*<out modreq(System.Runtime.InteropServices.OutAttribute) System.String, System.Void>",
                expectedSymbol: "delegate*<out modreq(System.Runtime.InteropServices.OutAttribute) System.String, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, parameterDecls[8],
                expectedSyntax: "delegate*<ref out string, void>",
                expectedType: "delegate*<ref System.String, System.Void>",
                expectedSymbol: "delegate*<ref System.String, System.Void>");
 
            MethodSymbol getParam(int index)
            {
                var type = ((FunctionPointerTypeSymbol)parameterTypes[index].Type);
                FunctionPointerUtilities.CommonVerifyFunctionPointer(type);
                return type.Signature;
            }
        }
 
        [Fact]
        public void VoidAsParameterType()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<void, void> p1) {}
}");
 
            comp.VerifyDiagnostics(
                    // (4,29): error CS1536: Invalid parameter type 'void'
                    //     void M(delegate*<void, void> p1) {}
                    Diagnostic(ErrorCode.ERR_NoVoidParameter, "void").WithLocation(4, 29));
 
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            var signature = ((FunctionPointerTypeSymbol)m.Parameters.Single().Type).Signature;
            Assert.True(signature.Parameters.Single().Type.IsVoidType());
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
            var paramType = syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<MethodDeclarationSyntax>()
                .Single().ParameterList.Parameters
                .Single().Type;
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, paramType!,
                expectedSyntax: "delegate*<void, void>",
                expectedType: "delegate*<System.Void, System.Void>",
                expectedSymbol: "delegate*<System.Void, System.Void>");
        }
 
        [Fact]
        public void Equality_ReturnVoid()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<void> p1,
           delegate*<void> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type, returnEquality: Equality.Equal, callingConventionEquality: Equality.Equal);
        }
 
        [Fact]
        public void EqualityDifferingNullability()
        {
            var comp = CreateFunctionPointerCompilation(@"
#nullable enable
class C
{
    unsafe void M(delegate*<string, string, string?> p1,
                  delegate*<string, string?, string> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.DifferingNullability, callingConventionEquality: Equality.Equal,
                Equality.Equal, Equality.DifferingNullability);
 
        }
 
        [Fact]
        public void EqualityMultipleParameters()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<string, object, C, int, void> p1,
                  delegate*<string, object, C, int, void> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.Equal, callingConventionEquality: Equality.Equal,
                Equality.Equal, Equality.Equal, Equality.Equal, Equality.Equal);
        }
 
        [Fact]
        public void EqualityNestedFunctionPointers()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<delegate*<string, object>, delegate*<C, int>> p1,
                  delegate*<delegate*<string, object>, delegate*<C, int>> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.Equal, callingConventionEquality: Equality.Equal,
                Equality.Equal);
        }
 
        [Fact]
        public void EqualityNestedFunctionPointersDifferingNullability()
        {
            var comp = CreateFunctionPointerCompilation(@"
#nullable enable
class C
{
    unsafe void M(delegate*<delegate*<string, object?>, delegate*<C?, int>> p1,
                  delegate*<delegate*<string?, object>, delegate*<C, int>> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.DifferingNullability, callingConventionEquality: Equality.Equal,
                Equality.DifferingNullability);
        }
 
        [Fact]
        public void Equality_ReturnNotEqual()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<string, object, C, int, string> p1,
                  delegate*<string, object, C, int, void> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.NotEqual, callingConventionEquality: Equality.Equal,
                Equality.Equal, Equality.Equal, Equality.Equal, Equality.Equal);
        }
 
        [Fact]
        public void Equality_ParameterTypeNotEqual()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<string, object, C, object, void> p1,
                  delegate*<string, object, C, int, void> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.Equal, callingConventionEquality: Equality.Equal,
                Equality.Equal, Equality.Equal, Equality.Equal, Equality.NotEqual);
        }
 
        [Fact]
        public void Equality_CallingConventionNotEqual()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate* unmanaged[Cdecl]<string, object, C, object, void> p1,
                  delegate* unmanaged[Thiscall]<string, object, C, object, void> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.Equal, callingConventionEquality: Equality.NotEqual,
                Equality.Equal, Equality.Equal, Equality.Equal, Equality.Equal);
        }
 
        [Fact]
        public void Equality_ParameterModifiersDifferent()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<ref string, object, C, object, void> p1,
           delegate*<string, in object, out C, object, void> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.Equal, callingConventionEquality: Equality.Equal,
                Equality.NotEqual, Equality.NotEqual, Equality.NotEqual, Equality.Equal);
        }
 
        [Fact]
        public void Equality_ReturnTypeDifferent()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<string, object, C, object, ref string> p1,
                  delegate*<string, object, C, object, ref readonly string> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.NotEqual, callingConventionEquality: Equality.Equal,
                Equality.Equal, Equality.Equal, Equality.Equal, Equality.Equal);
        }
 
        [Fact]
        public void Equality_InParameter()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<in string, void> p1,
                  delegate*<in string, void> p2) {}
}");
 
            comp.VerifyDiagnostics();
            var c = comp.GetTypeByMetadataName("C");
            var m = c.GetMethod("M");
            AssertEqualityAndHashCode((FunctionPointerTypeSymbol)m.Parameters[0].Type, (FunctionPointerTypeSymbol)m.Parameters[1].Type,
                returnEquality: Equality.Equal, callingConventionEquality: Equality.Equal,
                Equality.Equal);
        }
 
        enum Equality
        {
            Equal = 0,
            DifferingNullability = 0b01,
            NotEqual = 0b10
        }
 
        private void AssertEqualityAndHashCode(FunctionPointerTypeSymbol p1, FunctionPointerTypeSymbol p2, Equality returnEquality, Equality callingConventionEquality, params Equality[] parameterEqualities)
        {
            var overallEquality = returnEquality | callingConventionEquality | (parameterEqualities.Length > 0 ? parameterEqualities.Aggregate((acc, cur) => acc | cur) : 0);
 
            assertSymbolEquality(p1, p2, overallEquality);
            assertSymbolEquality(p1.Signature, p2.Signature, overallEquality);
 
            var ret1 = p1.Signature.ReturnTypeWithAnnotations;
            var ret2 = p2.Signature.ReturnTypeWithAnnotations;
            if (hasFlag(returnEquality, Equality.NotEqual))
            {
                if (p1.Signature.RefKind == p2.Signature.RefKind)
                {
                    Assert.False(ret1.Equals(ret2, TypeCompareKind.ConsiderEverything));
                    Assert.False(ret1.Equals(ret2, TypeCompareKind.AllNullableIgnoreOptions));
                }
            }
            else
            {
                Assert.Equal(p1.Signature.RefKind, p2.Signature.RefKind);
                Assert.True(ret1.Equals(ret2, TypeCompareKind.AllNullableIgnoreOptions));
                Assert.Equal(ret1.GetHashCode(), ret2.GetHashCode());
                Assert.Equal(returnEquality == Equality.Equal, ret1.Equals(ret2, TypeCompareKind.ConsiderEverything));
            }
 
            for (int i = 0; i < p1.Signature.ParameterCount; i++)
            {
                ParameterSymbol param1 = p1.Signature.Parameters[i];
                ParameterSymbol param2 = p2.Signature.Parameters[i];
                assertSymbolEquality(param1, param2, overallEquality);
                if (parameterEqualities[i] == Equality.Equal)
                {
                    Assert.True(((FunctionPointerParameterSymbol)param1).MethodEqualityChecks((FunctionPointerParameterSymbol)param2,
                                                                                              TypeCompareKind.ConsiderEverything));
                }
 
                for (int j = 0; j < p1.Signature.ParameterCount; j++)
                {
                    if (j == i) continue;
 
                    assertSymbolEquality(param1, p2.Signature.Parameters[j], Equality.NotEqual);
                    assertSymbolEquality(param2, p1.Signature.Parameters[j], Equality.NotEqual);
                }
            }
 
            static bool hasFlag(Equality eq, Equality flag) => (eq & flag) == flag;
            static void assertSymbolEquality(Symbol s1, Symbol s2, Equality eq)
            {
                if (hasFlag(eq, Equality.NotEqual))
                {
                    Assert.False(s1.Equals(s2, TypeCompareKind.ConsiderEverything));
                    Assert.False(s1.Equals(s2, TypeCompareKind.AllNullableIgnoreOptions));
                }
                else
                {
                    Assert.True(s1.Equals(s2, TypeCompareKind.AllNullableIgnoreOptions));
                    Assert.Equal(s1.GetHashCode(), s2.GetHashCode());
                    Assert.Equal(eq == Equality.Equal, s1.Equals(s2, TypeCompareKind.ConsiderEverything));
                }
            }
        }
 
        [Fact]
        public void Equality_DifferingRefKinds()
        {
            var comp = CreateFunctionPointerCompilation(@"
unsafe class C
{
    delegate*<ref object> ptr1Ref;
    delegate*<ref readonly object> ptr1RefReadonly;
    delegate*<ref object, void> ptr2Ref;
    delegate*<in object, void> ptr2In;
    delegate*<out object, void> ptr2Out;
}");
 
            var ptr1Ref = comp.GetMember<FieldSymbol>("C.ptr1Ref").Type;
            var ptr1RefReadonly = comp.GetMember<FieldSymbol>("C.ptr1RefReadonly").Type;
            var ptr2Ref = comp.GetMember<FieldSymbol>("C.ptr2Ref").Type;
            var ptr2In = comp.GetMember<FieldSymbol>("C.ptr2In").Type;
            var ptr2Out = comp.GetMember<FieldSymbol>("C.ptr2Out").Type;
 
            var symbolEqualityComparer = new SymbolEqualityComparer(
                TypeCompareKind.ConsiderEverything | TypeCompareKind.FunctionPointerRefMatchesOutInRefReadonly | TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds);
            Assert.Equal(ptr1Ref.GetPublicSymbol(), ptr1RefReadonly.GetPublicSymbol(), symbolEqualityComparer);
            Assert.Equal(ptr2Ref.GetPublicSymbol(), ptr2In.GetPublicSymbol(), symbolEqualityComparer);
            Assert.Equal(ptr2Ref.GetPublicSymbol(), ptr2Out.GetPublicSymbol(), symbolEqualityComparer);
            Assert.Equal(ptr2In.GetPublicSymbol(), ptr2Out.GetPublicSymbol(), symbolEqualityComparer);
 
            Assert.Equal(ptr1Ref.GetHashCode(), ptr1RefReadonly.GetHashCode());
            Assert.Equal(ptr2Ref.GetHashCode(), ptr2In.GetHashCode());
            Assert.Equal(ptr2Ref.GetHashCode(), ptr2Out.GetHashCode());
            Assert.Equal(symbolEqualityComparer.GetHashCode(ptr1Ref.GetPublicSymbol()), symbolEqualityComparer.GetHashCode(ptr1RefReadonly.GetPublicSymbol()));
            Assert.Equal(symbolEqualityComparer.GetHashCode(ptr2Ref.GetPublicSymbol()), symbolEqualityComparer.GetHashCode(ptr2In.GetPublicSymbol()));
            Assert.Equal(symbolEqualityComparer.GetHashCode(ptr2Ref.GetPublicSymbol()), symbolEqualityComparer.GetHashCode(ptr2Out.GetPublicSymbol()));
        }
 
        [Fact]
        public void NoInOutAttribute_NoInOutParameter()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<string, void> p1) {}
}");
 
            comp.MakeTypeMissing(WellKnownType.System_Runtime_InteropServices_InAttribute);
            comp.MakeTypeMissing(WellKnownType.System_Runtime_InteropServices_OutAttribute);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void NoInOutAttribute_InOutParameter()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M(delegate*<in string, out string, void> p1) {}
}");
 
            comp.MakeTypeMissing(WellKnownType.System_Runtime_InteropServices_InAttribute);
            comp.MakeTypeMissing(WellKnownType.System_Runtime_InteropServices_OutAttribute);
            comp.VerifyDiagnostics(
                // (4,29): error CS0518: Predefined type 'System.Runtime.InteropServices.InAttribute' is not defined or imported
                //     unsafe void M(delegate*<in string, out string, void> p1) {}
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "in string").WithArguments("System.Runtime.InteropServices.InAttribute").WithLocation(4, 29),
                // (4,40): error CS0518: Predefined type 'System.Runtime.InteropServices.OutAttribute' is not defined or imported
                //     unsafe void M(delegate*<in string, out string, void> p1) {}
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "out string").WithArguments("System.Runtime.InteropServices.OutAttribute").WithLocation(4, 40));
        }
 
        [Fact]
        public void DifferingModOpts()
        {
            var ilSource = @"
.class public auto ansi beforefieldinit Test1
       extends[mscorlib] System.Object
{
    .method public hidebysig instance void  M(method bool& *(int32&) noModopts,
                                              method bool modopt([mscorlib]System.Object)& *(int32&) modoptOnReturn,
                                              method bool& *(int32 modopt([mscorlib]System.Object)&) modoptOnParam,
                                              method bool& modopt([mscorlib]System.Object) *(int32&) modoptOnReturnRef,
                                              method bool& *(int32& modopt([mscorlib]System.Object)) modoptOnParamRef) cil managed
    {
      // Code size       2 (0x2)
      .maxstack  8
      IL_0000:  nop
      IL_0001:  ret
    } // end of method Test::M
}
";
 
            var comp = CreateCompilationWithIL("", ilSource, parseOptions: TestOptions.Regular9);
            var testClass = comp.GetTypeByMetadataName("Test1")!;
            var m = testClass.GetMethod("M");
 
            foreach (var param1 in m.Parameters)
            {
                foreach (var param2 in m.Parameters)
                {
                    if (!ReferenceEquals(param1, param2))
                    {
                        Assert.False(param1.Type.Equals(param2.Type, TypeCompareKind.ConsiderEverything));
                    }
                    else
                    {
                        Assert.True(param1.Type.Equals(param2.Type, TypeCompareKind.ConsiderEverything));
                    }
 
                    Assert.True(param1.Type.Equals(param2.Type, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                }
            }
        }
 
        [Fact]
        public void RequiresUnsafeInSignature()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    delegate*<void> _field;
    delegate*<void> Property { get; set; }
    delegate*<void> M(delegate*<void> param)
    {
        delegate*<void> local1;
        /**/delegate/**/*/**/<void> local2;
        throw null;
    }
}");
 
            comp.VerifyDiagnostics(
                    // (4,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                    //     delegate*<void> _field;
                    Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(4, 5),
                    // (4,21): warning CS0169: The field 'C._field' is never used
                    //     delegate*<void> _field;
                    Diagnostic(ErrorCode.WRN_UnreferencedField, "_field").WithArguments("C._field").WithLocation(4, 21),
                    // (5,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                    //     delegate*<void> Property { get; set; }
                    Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(5, 5),
                    // (6,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                    //     delegate*<void> M(delegate*<void> param)
                    Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(6, 5),
                    // (6,23): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                    //     delegate*<void> M(delegate*<void> param)
                    Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(6, 23),
                    // (8,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                    //         delegate*<void> local1;
                    Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(8, 9),
                    // (8,25): warning CS0168: The variable 'local1' is declared but never used
                    //         delegate*<void> local1;
                    Diagnostic(ErrorCode.WRN_UnreferencedVar, "local1").WithArguments("local1").WithLocation(8, 25),
                    // (9,13): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                    //         /**/delegate/**/*/**/<void> local2;
                    Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate/**/*").WithLocation(9, 13),
                    // (9,37): warning CS0168: The variable 'local1' is declared but never used
                    //         /**/delegate/**/*/**/<void> local2;
                    Diagnostic(ErrorCode.WRN_UnreferencedVar, "local2").WithArguments("local2").WithLocation(9, 37));
        }
 
        [Fact]
        public void MisdeclaredArraysWithLocalDeclarationsAreHandled()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    unsafe void M()
    {
        int a = 1;
        delegate*<int[a]> local;
    }
}");
 
            comp.VerifyDiagnostics(
                    // (6,13): warning CS0219: The variable 'a' is assigned but its value is never used
                    //         int a = 1;
                    Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "a").WithArguments("a").WithLocation(6, 13),
                    // (7,22): error CS0270: Array size cannot be specified in a variable declaration (try initializing with a 'new' expression)
                    //         delegate*<int[a]> local;
                    Diagnostic(ErrorCode.ERR_ArraySizeInDeclaration, "[a]").WithLocation(7, 22),
                    // (7,27): warning CS0168: The variable 'local' is declared but never used
                    //         delegate*<int[a]> local;
                    Diagnostic(ErrorCode.WRN_UnreferencedVar, "local").WithArguments("local").WithLocation(7, 27));
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
 
            var functionPointerTypeSyntax = syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<FunctionPointerTypeSyntax>()
                .Single();
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, functionPointerTypeSyntax,
                expectedSyntax: "delegate*<int[a]>",
                expectedType: "delegate*<System.Int32[]>",
                expectedSymbol: "delegate*<System.Int32[]>");
 
            var misplacedDeclaration =
                ((ArrayTypeSyntax)functionPointerTypeSyntax
                    .ParameterList.Parameters.Single().Type!)
                    .RankSpecifiers.Single()
                    .Sizes.Single();
 
            var a = (ILocalSymbol)model.GetSymbolInfo(misplacedDeclaration).Symbol!;
            Assert.NotNull(a);
            Assert.Equal("System.Int32 a", a.ToTestDisplayString());
 
            VerifyOperationTreeForNode(comp, model, functionPointerTypeSyntax.Parent, expectedOperationTree: @"
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'delegate*<int[a]> local')
  Ignored Dimensions(1):
      ILocalReferenceOperation: a (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'a')
  Declarators:
      IVariableDeclaratorOperation (Symbol: delegate*<System.Int32[]> local) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'local')
        Initializer: 
          null
  Initializer: 
    null
");
        }
 
        [Fact]
        public void IncorrectArguments()
        {
            var comp = CreateFunctionPointerCompilation(@"
unsafe class C
{
    void M(delegate*<void> p1,
           delegate*<string, void> p2,
           delegate*<string> p3,
           delegate*<ref string, void> p4,
           delegate*<in string, void> p5)
    {
        p1(""No arguments allowed"");
        p2(""Too"", ""many"", ""arguments"");
        p2(); // Not enough arguments
        p2(1); // Invalid argument type
        ref string foo = ref p3();
        string s = null;
        p4(s);
        p4(in s);
        p5(ref s);
    }
}");
 
            comp.VerifyDiagnostics(
                // (10,9): error CS8756: Function pointer 'delegate*<void>' does not take 1 arguments
                //         p1("No arguments allowed");
                Diagnostic(ErrorCode.ERR_BadFuncPointerArgCount, @"p1(""No arguments allowed"")").WithArguments("delegate*<void>", "1").WithLocation(10, 9),
                // (11,9): error CS8756: Function pointer 'delegate*<string, void>' does not take 3 arguments
                //         p2("Too", "many", "arguments");
                Diagnostic(ErrorCode.ERR_BadFuncPointerArgCount, @"p2(""Too"", ""many"", ""arguments"")").WithArguments("delegate*<string, void>", "3").WithLocation(11, 9),
                // (12,9): error CS8756: Function pointer 'delegate*<string, void>' does not take 0 arguments
                //         p2(); // Not enough arguments
                Diagnostic(ErrorCode.ERR_BadFuncPointerArgCount, "p2()").WithArguments("delegate*<string, void>", "0").WithLocation(12, 9),
                // (13,12): error CS1503: Argument 1: cannot convert from 'int' to 'string'
                //         p2(1); // Invalid argument type
                Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "string").WithLocation(13, 12),
                // (14,30): error CS1510: A ref or out value must be an assignable variable
                //         ref string foo = ref p3();
                Diagnostic(ErrorCode.ERR_RefLvalueExpected, "p3()").WithLocation(14, 30),
                // (16,12): error CS1620: Argument 1 must be passed with the 'ref' keyword
                //         p4(s);
                Diagnostic(ErrorCode.ERR_BadArgRef, "s").WithArguments("1", "ref").WithLocation(16, 12),
                // (17,15): error CS1620: Argument 1 must be passed with the 'ref' keyword
                //         p4(in s);
                Diagnostic(ErrorCode.ERR_BadArgRef, "s").WithArguments("1", "ref").WithLocation(17, 15),
                // (18,16): error CS9194: Argument 1 may not be passed with the 'ref' keyword in language version 9.0. To pass 'ref' arguments to 'in' parameters, upgrade to language version 12.0 or greater.
                //         p5(ref s);
                Diagnostic(ErrorCode.ERR_BadArgExtraRefLangVersion, "s").WithArguments("1", "9.0", "12.0").WithLocation(18, 16));
        }
 
        [Fact]
        public void InaccessibleNestedTypes()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    private class D {}
}
class E
{
    unsafe void M()
    {
        delegate*<C.D> d;
    }
}");
 
            comp.VerifyDiagnostics(
                    // (10,21): error CS0122: 'C.D' is inaccessible due to its protection level
                    //         delegate*<C.D> d;
                    Diagnostic(ErrorCode.ERR_BadAccess, "D").WithArguments("C.D").WithLocation(10, 21),
                    // (10,24): warning CS0168: The variable 'd' is declared but never used
                    //         delegate*<C.D> d;
                    Diagnostic(ErrorCode.WRN_UnreferencedVar, "d").WithArguments("d").WithLocation(10, 24));
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
 
            var functionPointerTypeSyntax = syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<FunctionPointerTypeSyntax>()
                .Single();
 
            Assert.Equal("delegate*<C.D>", functionPointerTypeSyntax.ToString());
 
            var typeInfo = model.GetTypeInfo(functionPointerTypeSyntax);
            Assert.Equal("delegate*<C.D>", typeInfo.Type.ToTestDisplayString());
            Assert.True(((IFunctionPointerTypeSymbol)typeInfo.Type!).Signature.ReturnType.IsErrorType());
 
            var nestedTypeInfo = model.GetTypeInfo(functionPointerTypeSyntax.ParameterList.Parameters.Single().Type!);
            Assert.Equal("C.D", nestedTypeInfo.Type!.ToTestDisplayString());
            Assert.False(nestedTypeInfo.Type!.IsErrorType());
 
            VerifyOperationTreeForNode(comp, model, functionPointerTypeSyntax.Parent, expectedOperationTree: @"
IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'delegate*<C.D> d')
  Declarators:
      IVariableDeclaratorOperation (Symbol: delegate*<C.D> d) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'd')
        Initializer: 
          null
  Initializer: 
    null
");
        }
 
        [Fact]
        public void FunctionPointerConstraintIntroducedBySubstitution()
        {
            string source = @"
class R1<T1>
{
    public virtual void f<T2>() where T2 : T1 { }
}
class R2 : R1<delegate*<void>>
{
    public override void f<T2>() { }
}
class Program
{
    static void Main(string[] args)
    {
        R2 r = new R2();
        r.f<int>();
    }
}";
 
            var compilation = CreateFunctionPointerCompilation(source);
            compilation.VerifyDiagnostics(
                // (6,7): error CS0306: The type 'delegate*<void>' may not be used as a type argument
                // class R2 : R1<delegate*<void>>
                Diagnostic(ErrorCode.ERR_BadTypeArgument, "R2").WithArguments("delegate*<void>").WithLocation(6, 7)
            );
 
            var syntaxTree = compilation.SyntaxTrees[0];
            var model = compilation.GetSemanticModel(syntaxTree);
 
            var baseNameSyntax = syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<SimpleBaseTypeSyntax>()
                .Single()
                .Type;
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, baseNameSyntax,
                expectedSyntax: "R1<delegate*<void>>",
                expectedType: "R1<delegate*<System.Void>>",
                expectedSymbol: "R1<delegate*<System.Void>>");
        }
 
        [Fact]
        public void FunctionPointerTypeAsThisOfExtensionMethod()
        {
            var comp = CreateFunctionPointerCompilation(@"
unsafe static class C
{
    static void M1(this delegate*<void> ptr) {}
    static void M2(delegate*<void> ptr)
    {
        ptr.M1();
    }
}");
 
            comp.VerifyDiagnostics(
                // (4,25): error CS1103: The first parameter of an extension method cannot be of type 'delegate*<void>'
                //     static void M1(this delegate*<void> ptr) {}
                Diagnostic(ErrorCode.ERR_BadTypeforThis, "delegate*<void>").WithArguments("delegate*<void>").WithLocation(4, 25)
            );
        }
 
        [Fact]
        public void FunctionPointerTypeAsThisOfExtensionMethod_DefinedInIl()
        {
            const string ilSource = @"
.class public auto ansi abstract sealed beforefieldinit CHelper
    extends [mscorlib]System.Object
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
        01 00 00 00
    )
    // Methods
    .method public hidebysig static 
        void M (
            method void*() i
        ) cil managed 
    {
        .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
            01 00 00 00
        )
        // Method begins at RVA 0x205c
        // Code size 9 (0x9)
        .maxstack 8
 
        IL_0001: ldc.i4.1
        IL_0002: call void [mscorlib]System.Console::WriteLine(int32)
        IL_0008: ret
    } // end of method CHelper::M
 
} // end of class CHelper
";
            const string source = @"
static class C
{
    static unsafe void Main()
    {
        delegate*<void> ptr = null;
        ptr.M();
    }
}";
            var comp = CreateCompilationWithIL(source, ilSource, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular9);
 
            var verifier = CompileAndVerify(comp, expectedOutput: "1", verify: Verification.Skipped);
            verifier.VerifyIL("C.Main", expectedIL: @"
{
  // Code size       10 (0xa)
  .maxstack  1
  .locals init (delegate*<void> V_0) //ptr
  IL_0000:  ldc.i4.0
  IL_0001:  conv.u
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  call       ""void CHelper.M(delegate*<void>)""
  IL_0009:  ret
}
");
        }
 
        [Fact]
        public void FunctionPointerTypeInAnonymousType()
        {
            var comp = CreateFunctionPointerCompilation(@"
unsafe static class C
{
    static void M(delegate*<void> ptr)
    {
        var a = new { Ptr = ptr };
        var b = new { Ptrs = new[] { ptr } };
    }
}");
 
            comp.VerifyDiagnostics(
                // (6,23): error CS0828: Cannot assign 'delegate*<void>' to anonymous type property
                //         var a = new { Ptr = ptr };
                Diagnostic(ErrorCode.ERR_AnonymousTypePropertyAssignedBadValue, "Ptr = ptr").WithArguments("delegate*<void>").WithLocation(6, 23)
                );
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
 
            var anonymousObjectCreations = syntaxTree.GetRoot()
                .DescendantNodes()
                .OfType<AnonymousObjectCreationExpressionSyntax>()
                .ToArray();
 
            Assert.Equal(2, anonymousObjectCreations.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, anonymousObjectCreations[0],
                expectedSyntax: "new { Ptr = ptr }",
                expectedType: "<anonymous type: delegate*<System.Void> Ptr>",
                expectedSymbol: "<anonymous type: delegate*<System.Void> Ptr>..ctor(delegate*<System.Void> Ptr)");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, anonymousObjectCreations[1],
                expectedSyntax: "new { Ptrs = new[] { ptr } }",
                expectedType: "<anonymous type: delegate*<System.Void>[] Ptrs>",
                expectedSymbol: "<anonymous type: delegate*<System.Void>[] Ptrs>..ctor(delegate*<System.Void>[] Ptrs)");
 
            VerifyOperationTreeForNode(comp, model, anonymousObjectCreations[0], expectedOperationTree: @"
IAnonymousObjectCreationOperation (OperationKind.AnonymousObjectCreation, Type: <anonymous type: delegate*<System.Void> Ptr>, IsInvalid) (Syntax: 'new { Ptr = ptr }')
  Initializers(1):
      ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: delegate*<System.Void>, IsInvalid) (Syntax: 'Ptr = ptr')
        Left: 
          IPropertyReferenceOperation: delegate*<System.Void> <anonymous type: delegate*<System.Void> Ptr>.Ptr { get; } (OperationKind.PropertyReference, Type: delegate*<System.Void>, IsInvalid) (Syntax: 'Ptr')
            Instance Receiver: 
              IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: <anonymous type: delegate*<System.Void> Ptr>, IsInvalid, IsImplicit) (Syntax: 'new { Ptr = ptr }')
        Right: 
          IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.Void>, IsInvalid) (Syntax: 'ptr')
");
 
            VerifyOperationTreeForNode(comp, model, anonymousObjectCreations[1], expectedOperationTree: @"
IAnonymousObjectCreationOperation (OperationKind.AnonymousObjectCreation, Type: <anonymous type: delegate*<System.Void>[] Ptrs>) (Syntax: 'new { Ptrs  ... ] { ptr } }')
  Initializers(1):
      ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: delegate*<System.Void>[]) (Syntax: 'Ptrs = new[] { ptr }')
        Left:
          IPropertyReferenceOperation: delegate*<System.Void>[] <anonymous type: delegate*<System.Void>[] Ptrs>.Ptrs { get; } (OperationKind.PropertyReference, Type: delegate*<System.Void>[]) (Syntax: 'Ptrs')
            Instance Receiver:
              IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: <anonymous type: delegate*<System.Void>[] Ptrs>, IsImplicit) (Syntax: 'new { Ptrs  ... ] { ptr } }')
        Right:
          IArrayCreationOperation (OperationKind.ArrayCreation, Type: delegate*<System.Void>[]) (Syntax: 'new[] { ptr }')
            Dimension Sizes(1):
                ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'new[] { ptr }')
            Initializer:
              IArrayInitializerOperation (1 elements) (OperationKind.ArrayInitializer, Type: null) (Syntax: '{ ptr }')
                Element Values(1):
                    IParameterReferenceOperation: ptr (OperationKind.ParameterReference, Type: delegate*<System.Void>) (Syntax: 'ptr')
");
        }
 
        [Fact]
        public void FunctionPointerTypeAsArgToIterator()
        {
            var comp = CreateFunctionPointerCompilation(@"
using System.Collections.Generic;
unsafe class C
{
    IEnumerable<int> Iterator1(delegate*<void> i)
    {
        yield return 1;
    }
 
    IEnumerable<int> Iterator2(delegate*<void>[] i)
    {
        yield return 1;
    }
}");
 
            comp.VerifyDiagnostics(
                // (5,48): error CS1637: Iterators cannot have pointer type parameters
                //     IEnumerable<int> Iterator1(delegate*<void> i)
                Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "i").WithLocation(5, 48)
                );
        }
 
        [Fact]
        public void FormattingReturnTypeOptions()
        {
            var il = @"
.class public auto ansi beforefieldinit C
       extends [mscorlib]System.Object
{
    .field public method int32& modreq([mscorlib]System.Runtime.InteropServices.InAttribute) *() 'Field1'
    .field public method int32 modopt([mscorlib]System.Object) & *() 'Field2'
}
";
 
            var comp = CreateCompilation("", references: new[] { CompileIL(il) });
            comp.VerifyDiagnostics();
 
            var c = comp.GetTypeByMetadataName("C");
            var f1 = c.GetField("Field1").Type;
            var f2 = c.GetField("Field2").Type;
 
            Assert.Equal("delegate*<ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.Int32>", f1.ToTestDisplayString());
            Assert.Equal("delegate*<ref readonly int>", f1.ToDisplayString());
            Assert.Equal("delegate*<ref System.Int32 modopt(System.Object)>", f2.ToTestDisplayString());
            Assert.Equal("delegate*<ref int>", f2.ToDisplayString());
        }
 
        [Fact]
        public void PublicApi_CreateInvalidInputs()
        {
            var comp = (Compilation)CreateCompilation("");
            var @string = comp.GetSpecialType(SpecialType.System_String);
            var cdeclType = comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl");
            Assert.NotNull(cdeclType);
            Assert.Throws<ArgumentNullException>("returnType", () => comp.CreateFunctionPointerTypeSymbol(returnType: null!, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty));
            Assert.Throws<ArgumentNullException>("parameterTypes", () => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: default, parameterRefKinds: ImmutableArray<RefKind>.Empty));
            Assert.Throws<ArgumentNullException>("parameterTypes[0]", () => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray.Create((ITypeSymbol?)null)!, parameterRefKinds: ImmutableArray.Create(RefKind.None)));
            Assert.Throws<ArgumentNullException>("parameterRefKinds", () => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: default));
            Assert.Throws<ArgumentNullException>("callingConventionTypes[0]", () => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.Unmanaged, ImmutableArray.Create((INamedTypeSymbol)null!)));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray.Create(RefKind.None)));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.Out, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty));
            Assert.Throws<ArgumentOutOfRangeException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: (SignatureCallingConvention)10));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.Default, callingConventionTypes: ImmutableArray.Create(cdeclType)!));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.StdCall, callingConventionTypes: ImmutableArray.Create(cdeclType)!));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.FastCall, callingConventionTypes: ImmutableArray.Create(cdeclType)!));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.CDecl, callingConventionTypes: ImmutableArray.Create(cdeclType)!));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.ThisCall, callingConventionTypes: ImmutableArray.Create(cdeclType)!));
            Assert.Throws<ArgumentException>(() => comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.Unmanaged, callingConventionTypes: ImmutableArray.Create(@string)!));
        }
 
        [Fact]
        public void PublicApi_VarargsHasUseSiteDiagnostic()
        {
            var comp = (Compilation)CreateCompilation("");
            var @string = comp.GetSpecialType(SpecialType.System_String);
            var ptr = comp.CreateFunctionPointerTypeSymbol(returnType: @string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, callingConvention: SignatureCallingConvention.VarArgs);
 
            Assert.Equal(SignatureCallingConvention.VarArgs, ptr.Signature.CallingConvention);
            var expectedMessage = "error CS8806: " + string.Format(CSharpResources.ERR_UnsupportedCallingConvention, "delegate* unmanaged[]<string>");
            AssertEx.Equal(expectedMessage, ptr.EnsureCSharpSymbolOrNull(nameof(ptr)).GetUseSiteDiagnostic().ToString());
        }
 
        [Fact]
        public void PublicApi_CreateTypeSymbolNoRefKinds()
        {
            var comp = (Compilation)CreateCompilation(@"class C {}");
 
            var c = comp.GetTypeByMetadataName("C")!;
 
            var ptr = comp.CreateFunctionPointerTypeSymbol(
                c.WithNullableAnnotation(CodeAnalysis.NullableAnnotation.None),
                RefKind.None,
                ImmutableArray.Create(c.WithNullableAnnotation(CodeAnalysis.NullableAnnotation.NotAnnotated), c.WithNullableAnnotation(CodeAnalysis.NullableAnnotation.Annotated)),
                ImmutableArray.Create(RefKind.None, RefKind.None));
 
            Assert.Equal("delegate*<C!, C?, C>", ptr.ToTestDisplayString(includeNonNullable: true));
        }
 
        [Fact]
        public void PublicApi_CreateInRefReadonlyTypeSymbol()
        {
            var comp = (Compilation)CreateCompilation("");
            var @string = comp.GetSpecialType(SpecialType.System_String);
 
            var ptr = comp.CreateFunctionPointerTypeSymbol(
                @string,
                RefKind.RefReadOnly,
                ImmutableArray.Create((ITypeSymbol)@string),
                ImmutableArray.Create(RefKind.In));
 
            Assert.Equal("delegate*<in modreq(System.Runtime.InteropServices.InAttribute) System.String, ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.String>",
                         ptr.ToTestDisplayString());
        }
 
        [Fact]
        public void PublicApi_CreateOutTypeSymbol()
        {
            var comp = (Compilation)CreateCompilation("");
            var @string = comp.GetSpecialType(SpecialType.System_String);
            var @void = comp.GetSpecialType(SpecialType.System_Void);
 
            var ptr = comp.CreateFunctionPointerTypeSymbol(
                @void,
                RefKind.None,
                ImmutableArray.Create((ITypeSymbol)@string),
                ImmutableArray.Create(RefKind.Out));
 
            Assert.Equal("delegate*<out modreq(System.Runtime.InteropServices.OutAttribute) System.String, System.Void>",
                         ptr.ToTestDisplayString());
        }
 
        [Fact]
        public void PublicApi_InOutAttributeMissing()
        {
            var comp = (Compilation)CreateCompilation("");
            comp.MakeTypeMissing(WellKnownType.System_Runtime_InteropServices_InAttribute);
            comp.MakeTypeMissing(WellKnownType.System_Runtime_InteropServices_OutAttribute);
            var @string = comp.GetSpecialType(SpecialType.System_String);
 
            var ptr = comp.CreateFunctionPointerTypeSymbol(
                @string,
                RefKind.RefReadOnly,
                ImmutableArray.Create((ITypeSymbol)@string),
                ImmutableArray.Create(RefKind.Out));
 
            Assert.Equal("System.Runtime.InteropServices.InAttribute[missing]", ptr.Signature.RefCustomModifiers.Single().Modifier.ToTestDisplayString());
            Assert.Equal("System.Runtime.InteropServices.OutAttribute[missing]", ptr.Signature.Parameters.Single().RefCustomModifiers.Single().Modifier.ToTestDisplayString());
        }
 
        [Theory]
        [InlineData("[Cdecl]", SignatureCallingConvention.CDecl)]
        [InlineData("[Thiscall]", SignatureCallingConvention.ThisCall)]
        [InlineData("[Stdcall]", SignatureCallingConvention.StdCall)]
        [InlineData("[Fastcall]", SignatureCallingConvention.FastCall)]
        [InlineData("", SignatureCallingConvention.Unmanaged)]
        public void PublicApi_CallingConventions_NoModopts(string expectedText, SignatureCallingConvention convention)
        {
            var comp = (Compilation)CreateCompilation("");
            var @string = comp.GetSpecialType(SpecialType.System_String);
 
            var ptr = comp.CreateFunctionPointerTypeSymbol(@string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, convention);
            AssertEx.Equal($"delegate* unmanaged{expectedText}<System.String>", ptr.ToTestDisplayString());
            ptr = comp.CreateFunctionPointerTypeSymbol(@string, returnRefKind: RefKind.RefReadOnly, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, convention);
            AssertEx.Equal($"delegate* unmanaged{expectedText}<ref readonly modreq(System.Runtime.InteropServices.InAttribute) System.String>", ptr.ToTestDisplayString());
        }
 
        [Fact]
        public void PublicApi_CallingConventions_Modopts()
        {
            var comp = (Compilation)CreateCompilation("");
            var @string = comp.GetSpecialType(SpecialType.System_String);
            var cdeclType = comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl");
            var stdcallType = comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall");
            Assert.NotNull(cdeclType);
 
            var ptr = comp.CreateFunctionPointerTypeSymbol(@string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, SignatureCallingConvention.Unmanaged, ImmutableArray.Create(cdeclType, stdcallType)!);
            AssertEx.Equal("delegate* unmanaged[Cdecl, Stdcall]<System.String modopt(System.Runtime.CompilerServices.CallConvCdecl) modopt(System.Runtime.CompilerServices.CallConvStdcall)>", ptr.ToTestDisplayString());
            ptr = comp.CreateFunctionPointerTypeSymbol(@string, returnRefKind: RefKind.RefReadOnly, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, SignatureCallingConvention.Unmanaged, ImmutableArray.Create(cdeclType, stdcallType)!);
            AssertEx.Equal("delegate* unmanaged[Cdecl, Stdcall]<ref readonly modopt(System.Runtime.CompilerServices.CallConvCdecl) modopt(System.Runtime.CompilerServices.CallConvStdcall) modreq(System.Runtime.InteropServices.InAttribute) System.String>", ptr.ToTestDisplayString());
 
            ptr = comp.CreateFunctionPointerTypeSymbol(@string, returnRefKind: RefKind.None, parameterTypes: ImmutableArray<ITypeSymbol>.Empty, parameterRefKinds: ImmutableArray<RefKind>.Empty, SignatureCallingConvention.Unmanaged, ImmutableArray.Create(cdeclType)!);
            AssertEx.Equal("delegate* unmanaged[Cdecl]<System.String modopt(System.Runtime.CompilerServices.CallConvCdecl)>", ptr.ToTestDisplayString());
            Assert.Equal(SignatureCallingConvention.Unmanaged, ptr.Signature.CallingConvention);
        }
 
        [Fact]
        public void PublicApi_SemanticInfo01()
        {
            var comp = CreateFunctionPointerCompilation(@"
unsafe class C
{
    public static string M1(C c) => null;
    public static void M2(string s, int i) {}
    public delegate*<string, int, void> M(delegate*<C, string> ptr)
    {
        delegate*<string, int, void> ptr2 = &M2;
        ptr = &M1;
        ptr(null);
        return &M2;
    }
}");
 
            comp.VerifyDiagnostics();
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
 
            var mDeclSyntax = syntaxTree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Skip(2).Single();
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, mDeclSyntax.ReturnType,
                expectedSyntax: "delegate*<string, int, void>",
                expectedType: "delegate*<System.String, System.Int32, System.Void>",
                expectedSymbol: "delegate*<System.String, System.Int32, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, mDeclSyntax.ParameterList.Parameters[0].Type!,
                expectedSyntax: "delegate*<C, string>",
                expectedType: "delegate*<C, System.String>",
                expectedSymbol: "delegate*<C, System.String>");
 
            var varDecl = mDeclSyntax.DescendantNodes().OfType<VariableDeclarationSyntax>().Single();
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, varDecl.Type,
                expectedSyntax: "delegate*<string, int, void>",
                expectedType: "delegate*<System.String, System.Int32, System.Void>",
                expectedSymbol: "delegate*<System.String, System.Int32, System.Void>");
 
            var varInitializer = varDecl.Variables.Single().Initializer!.Value;
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, varInitializer,
                expectedSyntax: "&M2",
                expectedType: null,
                expectedConvertedType: "delegate*<System.String, System.Int32, System.Void>",
                expectedSymbol: "void C.M2(System.String s, System.Int32 i)");
 
            var assignment = mDeclSyntax.DescendantNodes().OfType<AssignmentExpressionSyntax>().Single();
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, assignment,
                expectedSyntax: "ptr = &M1",
                expectedType: "delegate*<C, System.String>",
                expectedSymbol: null,
                expectedSymbolCandidates: null);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, assignment.Left,
                expectedSyntax: "ptr",
                expectedType: "delegate*<C, System.String>",
                expectedSymbol: "delegate*<C, System.String> ptr");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, assignment.Right,
                expectedSyntax: "&M1",
                expectedType: null,
                expectedConvertedType: "delegate*<C, System.String>",
                expectedSymbol: "System.String C.M1(C c)");
 
            InvocationExpressionSyntax invocationExpressionSyntax = mDeclSyntax.DescendantNodes().OfType<InvocationExpressionSyntax>().Single();
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, invocationExpressionSyntax,
                expectedSyntax: "ptr(null)",
                expectedType: "System.String",
                expectedSymbol: "delegate*<C, System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, invocationExpressionSyntax.Expression,
                expectedSyntax: "ptr",
                expectedType: "delegate*<C, System.String>",
                expectedSymbol: "delegate*<C, System.String> ptr");
 
            var typeInfo = model.GetTypeInfo(invocationExpressionSyntax);
            Assert.Equal("System.String", typeInfo.Type.ToTestDisplayString());
            Assert.Equal("System.String", typeInfo.ConvertedType.ToTestDisplayString());
 
            var returnExpression = mDeclSyntax.DescendantNodes().OfType<ReturnStatementSyntax>().Single().Expression!;
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model,
                returnExpression,
                expectedSyntax: "&M2",
                expectedType: null,
                expectedConvertedType: "delegate*<System.String, System.Int32, System.Void>",
                expectedSymbol: "void C.M2(System.String s, System.Int32 i)");
        }
 
        [Fact]
        public void PublicApi_SemanticInfo02()
        {
            var comp = CreateFunctionPointerCompilation(@"
class C
{
    public static string M1(int i) => null;
    public unsafe void M2()
    {
        delegate*<int, string> ptr = &M1;
        delegate*<int, void> ptr2 = &M1;
        _ = ptr(1);
        _ = ptr();
    }
 
    public void M3()
    {
        delegate*<int, string> ptr = &M1;
        _ = ptr(1);
    }
}
");
 
            comp.VerifyDiagnostics(
                // (8,38): error CS0407: 'string C.M1(int)' has the wrong return type
                //         delegate*<int, void> ptr2 = &M1;
                Diagnostic(ErrorCode.ERR_BadRetType, "M1").WithArguments("C.M1(int)", "string").WithLocation(8, 38),
                // (10,13): error CS8756: Function pointer 'delegate*<int, string>' does not take 0 arguments
                //         _ = ptr();
                Diagnostic(ErrorCode.ERR_BadFuncPointerArgCount, "ptr()").WithArguments("delegate*<int, string>", "0").WithLocation(10, 13),
                // (15,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                //         delegate*<int, string> ptr = &M1;
                Diagnostic(ErrorCode.ERR_UnsafeNeeded, "delegate*").WithLocation(15, 9),
                // (16,13): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                //         _ = ptr(1);
                Diagnostic(ErrorCode.ERR_UnsafeNeeded, "ptr(1)").WithLocation(16, 13)
            );
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
 
            var methodDecls = syntaxTree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().ToArray();
 
            var ptrTypes = methodDecls
                .SelectMany(m => m.DescendantNodes().OfType<FunctionPointerTypeSyntax>())
                .ToArray();
 
            Assert.Equal(3, ptrTypes.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, ptrTypes[0],
                expectedSyntax: "delegate*<int, string>",
                expectedType: "delegate*<System.Int32, System.String>",
                expectedSymbol: "delegate*<System.Int32, System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, ptrTypes[1],
                expectedSyntax: "delegate*<int, void>",
                expectedType: "delegate*<System.Int32, System.Void>",
                expectedSymbol: "delegate*<System.Int32, System.Void>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, ptrTypes[2],
                expectedSyntax: "delegate*<int, string>",
                expectedType: "delegate*<System.Int32, System.String>",
                expectedSymbol: "delegate*<System.Int32, System.String>");
 
            var m2DeclSyntax = methodDecls[1];
            var decls = m2DeclSyntax.DescendantNodes().OfType<VariableDeclaratorSyntax>().ToArray();
 
            Assert.Equal("ptr = &M1", decls[0].ToString());
            var addressOfSyntax = (PrefixUnaryExpressionSyntax)decls[0].Initializer!.Value;
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, addressOfSyntax,
                expectedSyntax: "&M1",
                expectedType: null,
                expectedConvertedType: "delegate*<System.Int32, System.String>",
                expectedSymbol: "System.String C.M1(System.Int32 i)");
 
            Assert.Equal("ptr2 = &M1", decls[1].ToString());
            addressOfSyntax = (PrefixUnaryExpressionSyntax)decls[1].Initializer!.Value;
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, addressOfSyntax,
                expectedSyntax: "&M1",
                expectedType: null,
                expectedConvertedType: "delegate*<System.Int32, System.Void>",
                expectedCandidateReason: CandidateReason.OverloadResolutionFailure,
                expectedSymbolCandidates: new[] { "System.String C.M1(System.Int32 i)" });
 
            var invocations = m2DeclSyntax.DescendantNodes().OfType<InvocationExpressionSyntax>().ToArray();
            Assert.Equal(2, invocations.Length);
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, invocations[0],
                expectedSyntax: "ptr(1)",
                expectedType: "System.String",
                expectedSymbol: "delegate*<System.Int32, System.String>");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, invocations[0].Expression,
                expectedSyntax: "ptr",
                expectedType: "delegate*<System.Int32, System.String>",
                expectedSymbol: "delegate*<System.Int32, System.String> ptr");
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, invocations[1],
                expectedSyntax: "ptr()",
                expectedType: "System.String",
                expectedCandidateReason: CandidateReason.OverloadResolutionFailure,
                expectedSymbolCandidates: new[] { "delegate*<System.Int32, System.String>" });
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, invocations[1].Expression,
                expectedSyntax: "ptr",
                expectedType: "delegate*<System.Int32, System.String>",
                expectedSymbol: "delegate*<System.Int32, System.String> ptr");
 
            var m3DeclSyntax = methodDecls[2];
 
            var variableDeclaratorSyntax = m3DeclSyntax.DescendantNodes().OfType<VariableDeclaratorSyntax>().Single();
            Assert.Equal("ptr = &M1", variableDeclaratorSyntax.ToString());
 
            var initializerValue = variableDeclaratorSyntax.Initializer!.Value;
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, initializerValue,
                expectedSyntax: "&M1",
                expectedType: null,
                expectedConvertedType: "delegate*<System.Int32, System.String>",
                expectedSymbol: "System.String C.M1(System.Int32 i)");
 
            var invocationExpr = m3DeclSyntax.DescendantNodes().OfType<InvocationExpressionSyntax>().Single();
 
            FunctionPointerUtilities.VerifyFunctionPointerSemanticInfo(model, invocationExpr,
                expectedSyntax: "ptr(1)",
                expectedType: "System.String",
                expectedSymbol: "delegate*<System.Int32, System.String>");
        }
 
        [Fact]
        public void PublicApi_DeclaredSymbol_BadSymbols()
        {
            var comp = CreateFunctionPointerCompilation(@"
#pragma warning disable CS0168 // Unused local
unsafe class C
{
    void M()
    {
        delegate*<out int> ptr1;
        delegate*<in int> ptr2;
        delegate*<ref readonly int, void> ptr3;
        delegate*<void, void> ptr4;
        delegate*<ref void> ptr5;
    }
}
");
 
            comp.VerifyDiagnostics(
                // (7,19): error CS8808: 'out' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                //         delegate*<out int> ptr1;
                Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "out").WithArguments("out").WithLocation(7, 19),
                // (8,19): error CS8808: 'in' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.
                //         delegate*<in int> ptr2;
                Diagnostic(ErrorCode.ERR_InvalidFuncPointerReturnTypeModifier, "in").WithArguments("in").WithLocation(8, 19),
                // (9,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.RequiresLocationAttribute' is not defined or imported
                //         delegate*<ref readonly int, void> ptr3;
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "ref readonly int").WithArguments("System.Runtime.CompilerServices.RequiresLocationAttribute").WithLocation(9, 19),
                // (9,23): error CS8773: Feature 'ref readonly parameters' is not available in C# 9.0. Please use language version 12.0 or greater.
                //         delegate*<ref readonly int, void> ptr3;
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "readonly").WithArguments("ref readonly parameters", "12.0").WithLocation(9, 23),
                // (10,19): error CS1536: Invalid parameter type 'void'
                //         delegate*<void, void> ptr4;
                Diagnostic(ErrorCode.ERR_NoVoidParameter, "void").WithLocation(10, 19),
                // (11,19): error CS1547: Keyword 'void' cannot be used in this context
                //         delegate*<ref void> ptr5;
                Diagnostic(ErrorCode.ERR_NoVoidHere, "ref void").WithLocation(11, 19));
 
            var syntaxTree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(syntaxTree);
 
            var decls = syntaxTree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().ToArray();
 
            Assert.Equal(5, decls.Length);
 
            Assert.Equal("delegate*<System.Int32> ptr1", model.GetDeclaredSymbol(decls[0]).ToTestDisplayString());
            Assert.Equal("delegate*<System.Int32> ptr2", model.GetDeclaredSymbol(decls[1]).ToTestDisplayString());
            Assert.Equal("delegate*<ref readonly modopt(System.Runtime.CompilerServices.RequiresLocationAttribute[missing]) System.Int32, System.Void> ptr3", model.GetDeclaredSymbol(decls[2]).ToTestDisplayString());
            Assert.Equal("delegate*<System.Void, System.Void> ptr4", model.GetDeclaredSymbol(decls[3]).ToTestDisplayString());
            Assert.Equal("delegate*<ref System.Void> ptr5", model.GetDeclaredSymbol(decls[4]).ToTestDisplayString());
        }
 
        [Fact]
        public void PublicApi_NonApplicationCorLibrary()
        {
            var otherCorLib = CreateEmptyCompilation(@"
namespace System
{
    public class Object { }
    public abstract class ValueType { }
    public struct Void { }
    public class String { }
    namespace Runtime.CompilerServices
    {
        internal class CallConvTest {}
        public static class RuntimeFeature
        {
            public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention);
        }
    }
}
", options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9);
 
            var mainComp = CreateCompilation("");
            var returnType = mainComp.GetSpecialType(SpecialType.System_String).GetPublicSymbol();
            var testConvention = otherCorLib.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvTest");
            Assert.NotNull(testConvention);
            Assert.NotSame(testConvention!.ContainingAssembly.CorLibrary, mainComp.Assembly.CorLibrary);
            Assert.True(FunctionPointerTypeSymbol.IsCallingConventionModifier(testConvention));
 
            Assert.Throws<ArgumentException>(() => mainComp.CreateFunctionPointerTypeSymbol(
                returnType!,
                returnRefKind: RefKind.None,
                parameterTypes: ImmutableArray<ITypeSymbol>.Empty,
                parameterRefKinds: ImmutableArray<RefKind>.Empty,
                callingConvention: SignatureCallingConvention.Unmanaged,
                callingConventionTypes: ImmutableArray.Create(testConvention.GetPublicSymbol()!)));
        }
 
        [Fact]
        public void Equality_UnmanagedExtensionModifiers()
        {
            var comp = CreateFunctionPointerCompilation("");
 
            var returnType = comp.GetSpecialType(SpecialType.System_String);
 
            var objectMod = CSharpCustomModifier.CreateOptional(comp.GetSpecialType(SpecialType.System_Object));
            var thiscallMod = CSharpCustomModifier.CreateOptional(comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvThiscall"));
            var stdcallMod = CSharpCustomModifier.CreateOptional(comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall"));
 
            var funcPtrPlatformDefault = createTypeSymbol(customModifiers: default);
            var funcPtrConventionThisCall = createTypeSymbol(customModifiers: default, CallingConvention.ThisCall);
            var funcPtrConventionThisCallWithThiscallMod = createTypeSymbol(customModifiers: ImmutableArray.Create(thiscallMod), CallingConvention.ThisCall);
 
            var funcPtrThiscall = createTypeSymbol(customModifiers: ImmutableArray.Create(thiscallMod));
            var funcPtrThiscallObject = createTypeSymbol(customModifiers: ImmutableArray.Create(thiscallMod, objectMod));
            var funcPtrObjectThiscall = createTypeSymbol(customModifiers: ImmutableArray.Create(objectMod, thiscallMod));
            var funcPtrObjectThiscallObject = createTypeSymbol(customModifiers: ImmutableArray.Create(objectMod, thiscallMod, objectMod));
 
            var funcPtrThiscallStdcall = createTypeSymbol(customModifiers: ImmutableArray.Create(thiscallMod, stdcallMod));
            var funcPtrStdcallThiscall = createTypeSymbol(customModifiers: ImmutableArray.Create(stdcallMod, thiscallMod));
            var funcPtrThiscallThiscallStdcall = createTypeSymbol(customModifiers: ImmutableArray.Create(thiscallMod, thiscallMod, stdcallMod));
            var funcPtrThiscallObjectStdcall = createTypeSymbol(customModifiers: ImmutableArray.Create(thiscallMod, objectMod, stdcallMod));
 
            verifyEquality(funcPtrPlatformDefault, funcPtrThiscall, expectedConventionEquality: false, expectedFullEquality: false);
            verifyEquality(funcPtrPlatformDefault, funcPtrConventionThisCall, expectedConventionEquality: false, expectedFullEquality: false, skipGetCallingConventionModifiersCheck: true);
            verifyEquality(funcPtrConventionThisCallWithThiscallMod, funcPtrConventionThisCall, expectedConventionEquality: true, expectedFullEquality: false, skipGetCallingConventionModifiersCheck: true);
 
            // Single calling convention modopt
            verifyEquality(funcPtrThiscall, funcPtrThiscallObject, expectedConventionEquality: true, expectedFullEquality: false);
            verifyEquality(funcPtrThiscall, funcPtrObjectThiscall, expectedConventionEquality: true, expectedFullEquality: false);
            verifyEquality(funcPtrThiscall, funcPtrObjectThiscallObject, expectedConventionEquality: true, expectedFullEquality: false);
 
            verifyEquality(funcPtrThiscallObject, funcPtrObjectThiscall, expectedConventionEquality: true, expectedFullEquality: false);
            verifyEquality(funcPtrThiscallObject, funcPtrObjectThiscallObject, expectedConventionEquality: true, expectedFullEquality: false);
 
            verifyEquality(funcPtrObjectThiscall, funcPtrObjectThiscallObject, expectedConventionEquality: true, expectedFullEquality: false);
 
            // Multiple calling convention modopts
            verifyEquality(funcPtrThiscallStdcall, funcPtrStdcallThiscall, expectedConventionEquality: true, expectedFullEquality: false);
            verifyEquality(funcPtrThiscallStdcall, funcPtrThiscallThiscallStdcall, expectedConventionEquality: true, expectedFullEquality: false);
            verifyEquality(funcPtrThiscallStdcall, funcPtrThiscallObjectStdcall, expectedConventionEquality: true, expectedFullEquality: false);
 
            verifyEquality(funcPtrStdcallThiscall, funcPtrThiscallThiscallStdcall, expectedConventionEquality: true, expectedFullEquality: false);
            verifyEquality(funcPtrStdcallThiscall, funcPtrThiscallObjectStdcall, expectedConventionEquality: true, expectedFullEquality: false);
 
            verifyEquality(funcPtrThiscallThiscallStdcall, funcPtrThiscallObjectStdcall, expectedConventionEquality: true, expectedFullEquality: false);
 
            static void verifyEquality((FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) ptr1, (FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) ptr2, bool expectedConventionEquality, bool expectedFullEquality, bool skipGetCallingConventionModifiersCheck = false)
            {
                // No equality between pointers with differing refkinds
                Assert.False(ptr1.NoRef.Equals(ptr2.ByRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.False(ptr1.NoRef.Equals(ptr2.ByRef, TypeCompareKind.ConsiderEverything));
                Assert.False(ptr1.ByRef.Equals(ptr2.NoRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.False(ptr1.ByRef.Equals(ptr2.NoRef, TypeCompareKind.ConsiderEverything));
 
                if (!skipGetCallingConventionModifiersCheck)
                {
                    Assert.Equal(expectedConventionEquality, ptr1.NoRef.Signature.GetCallingConventionModifiers().SetEquals(ptr2.NoRef.Signature.GetCallingConventionModifiers()));
                    Assert.Equal(expectedConventionEquality, ptr1.ByRef.Signature.GetCallingConventionModifiers().SetEquals(ptr2.ByRef.Signature.GetCallingConventionModifiers()));
                }
                Assert.Equal(expectedConventionEquality, ptr1.NoRef.Equals(ptr2.NoRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.Equal(expectedConventionEquality, ptr1.ByRef.Equals(ptr2.ByRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.Equal(expectedFullEquality, ptr1.NoRef.Equals(ptr2.NoRef, TypeCompareKind.ConsiderEverything));
                Assert.Equal(expectedFullEquality, ptr1.ByRef.Equals(ptr2.ByRef, TypeCompareKind.ConsiderEverything));
            }
 
            (FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) createTypeSymbol(ImmutableArray<CustomModifier> customModifiers, CallingConvention callingConvention = CallingConvention.Unmanaged)
                => (FunctionPointerTypeSymbol.CreateFromPartsForTests(
                        callingConvention,
                        TypeWithAnnotations.Create(returnType, customModifiers: customModifiers),
                        refCustomModifiers: default,
                        returnRefKind: RefKind.None,
                        parameterTypes: ImmutableArray<TypeWithAnnotations>.Empty,
                        parameterRefCustomModifiers: default,
                        parameterRefKinds: ImmutableArray<RefKind>.Empty,
                        compilation: comp),
                    FunctionPointerTypeSymbol.CreateFromPartsForTests(
                        callingConvention,
                        TypeWithAnnotations.Create(returnType),
                        customModifiers,
                        RefKind.Ref,
                        parameterTypes: ImmutableArray<TypeWithAnnotations>.Empty,
                        parameterRefCustomModifiers: default,
                        parameterRefKinds: ImmutableArray<RefKind>.Empty,
                        compilation: comp));
        }
 
        [Fact]
        public void CallingConventionNamedCallConv()
        {
            var comp = CreateEmptyCompilation(@"
namespace System
{
    public class Object { }
    public abstract class ValueType { }
    public struct Void { }
    public class String { }
    namespace Runtime.CompilerServices
    {
        internal class CallConv {}
        public static class RuntimeFeature
        {
            public const string UnmanagedSignatureCallingConvention = nameof(UnmanagedSignatureCallingConvention);
        }
    }
}
", options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9);
 
            var returnType = comp.GetSpecialType(SpecialType.System_String);
 
            var callConvMod = CSharpCustomModifier.CreateOptional(comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConv"));
 
            var funcPtr = createTypeSymbol(customModifiers: default);
            var funcPtrCallConv = createTypeSymbol(customModifiers: ImmutableArray.Create(callConvMod));
 
            verifyEquality(funcPtr, funcPtrCallConv, expectedConventionEquality: true, expectedFullEquality: false);
 
            static void verifyEquality((FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) ptr1, (FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) ptr2, bool expectedConventionEquality, bool expectedFullEquality)
            {
                // No equality between pointers with differing refkinds
                Assert.False(ptr1.NoRef.Equals(ptr2.ByRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.False(ptr1.NoRef.Equals(ptr2.ByRef, TypeCompareKind.ConsiderEverything));
                Assert.False(ptr1.ByRef.Equals(ptr2.NoRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.False(ptr1.ByRef.Equals(ptr2.NoRef, TypeCompareKind.ConsiderEverything));
 
                Assert.Equal(expectedConventionEquality, ptr1.NoRef.Signature.GetCallingConventionModifiers().SetEquals(ptr2.NoRef.Signature.GetCallingConventionModifiers()));
                Assert.Equal(expectedConventionEquality, ptr1.ByRef.Signature.GetCallingConventionModifiers().SetEquals(ptr2.ByRef.Signature.GetCallingConventionModifiers()));
                Assert.Equal(expectedConventionEquality, ptr1.NoRef.Equals(ptr2.NoRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.Equal(expectedConventionEquality, ptr1.ByRef.Equals(ptr2.ByRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.Equal(expectedFullEquality, ptr1.NoRef.Equals(ptr2.NoRef, TypeCompareKind.ConsiderEverything));
                Assert.Equal(expectedFullEquality, ptr1.ByRef.Equals(ptr2.ByRef, TypeCompareKind.ConsiderEverything));
            }
 
            (FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) createTypeSymbol(ImmutableArray<CustomModifier> customModifiers, CallingConvention callingConvention = CallingConvention.Unmanaged)
                => (FunctionPointerTypeSymbol.CreateFromPartsForTests(
                        callingConvention,
                        TypeWithAnnotations.Create(returnType, customModifiers: customModifiers),
                        refCustomModifiers: default,
                        returnRefKind: RefKind.None,
                        parameterTypes: ImmutableArray<TypeWithAnnotations>.Empty,
                        parameterRefCustomModifiers: default,
                        parameterRefKinds: ImmutableArray<RefKind>.Empty,
                        compilation: comp),
                    FunctionPointerTypeSymbol.CreateFromPartsForTests(
                        callingConvention,
                        TypeWithAnnotations.Create(returnType),
                        customModifiers,
                        RefKind.Ref,
                        parameterTypes: ImmutableArray<TypeWithAnnotations>.Empty,
                        parameterRefCustomModifiers: default,
                        parameterRefKinds: ImmutableArray<RefKind>.Empty,
                        compilation: comp));
        }
 
        [Fact]
        public void Equality_DifferingRefAndTypeCustomModifiers()
        {
            var comp = CreateFunctionPointerCompilation("");
 
            var returnType = comp.GetSpecialType(SpecialType.System_String);
 
            var objectMod = CSharpCustomModifier.CreateOptional(comp.GetSpecialType(SpecialType.System_Object));
            var thiscallMod = CSharpCustomModifier.CreateOptional(comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvThiscall"));
            var stdcallMod = CSharpCustomModifier.CreateOptional(comp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall"));
 
            var funcPtrThiscallOnTypeThiscallOnRef = createTypeSymbol(typeCustomModifiers: ImmutableArray.Create(thiscallMod), refCustomModifiers: ImmutableArray.Create(thiscallMod));
            var funcPtrThiscallOnTypeStdcallOnRef = createTypeSymbol(typeCustomModifiers: ImmutableArray.Create(thiscallMod), refCustomModifiers: ImmutableArray.Create(stdcallMod));
            var funcPtrStdcallOnTypeThiscallOnRef = createTypeSymbol(typeCustomModifiers: ImmutableArray.Create(stdcallMod), refCustomModifiers: ImmutableArray.Create(thiscallMod));
 
            verifyEquality(funcPtrThiscallOnTypeThiscallOnRef, funcPtrThiscallOnTypeStdcallOnRef, expectedTypeConventionEquality: true, expectedRefConventionEquality: false);
            verifyEquality(funcPtrThiscallOnTypeThiscallOnRef, funcPtrStdcallOnTypeThiscallOnRef, expectedTypeConventionEquality: false, expectedRefConventionEquality: true);
            verifyEquality(funcPtrThiscallOnTypeStdcallOnRef, funcPtrStdcallOnTypeThiscallOnRef, expectedTypeConventionEquality: false, expectedRefConventionEquality: false);
 
            static void verifyEquality((FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) ptr1, (FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) ptr2, bool expectedTypeConventionEquality, bool expectedRefConventionEquality)
            {
                // No equality between pointers with differing refkinds
                Assert.False(ptr1.NoRef.Equals(ptr2.ByRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.False(ptr1.NoRef.Equals(ptr2.ByRef, TypeCompareKind.ConsiderEverything));
                Assert.False(ptr1.ByRef.Equals(ptr2.NoRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.False(ptr1.ByRef.Equals(ptr2.NoRef, TypeCompareKind.ConsiderEverything));
 
                Assert.Equal(expectedTypeConventionEquality, ptr1.NoRef.Signature.GetCallingConventionModifiers().SetEquals(ptr2.NoRef.Signature.GetCallingConventionModifiers()));
                Assert.Equal(expectedRefConventionEquality, ptr1.ByRef.Signature.GetCallingConventionModifiers().SetEquals(ptr2.ByRef.Signature.GetCallingConventionModifiers()));
 
                Assert.Equal(expectedTypeConventionEquality, ptr1.NoRef.Equals(ptr2.NoRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                Assert.Equal(expectedRefConventionEquality, ptr1.ByRef.Equals(ptr2.ByRef, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds));
                // If we weren't expected the ref version to be equal, but we were expecting the type version to be equal, then that means
                // the type version will be identical because it will have no ref modifiers
                Assert.Equal(expectedTypeConventionEquality && !expectedRefConventionEquality, ptr1.NoRef.Equals(ptr2.NoRef, TypeCompareKind.ConsiderEverything));
                Assert.False(ptr1.ByRef.Equals(ptr2.ByRef, TypeCompareKind.ConsiderEverything));
            }
 
            (FunctionPointerTypeSymbol NoRef, FunctionPointerTypeSymbol ByRef) createTypeSymbol(ImmutableArray<CustomModifier> typeCustomModifiers, ImmutableArray<CustomModifier> refCustomModifiers, CallingConvention callingConvention = CallingConvention.Unmanaged)
                => (FunctionPointerTypeSymbol.CreateFromPartsForTests(
                        callingConvention,
                        TypeWithAnnotations.Create(returnType, customModifiers: typeCustomModifiers),
                        refCustomModifiers: default,
                        returnRefKind: RefKind.None,
                        parameterTypes: ImmutableArray<TypeWithAnnotations>.Empty,
                        parameterRefCustomModifiers: default,
                        parameterRefKinds: ImmutableArray<RefKind>.Empty,
                        compilation: comp),
                    FunctionPointerTypeSymbol.CreateFromPartsForTests(
                        callingConvention,
                        TypeWithAnnotations.Create(returnType, customModifiers: typeCustomModifiers),
                        refCustomModifiers,
                        RefKind.Ref,
                        parameterTypes: ImmutableArray<TypeWithAnnotations>.Empty,
                        parameterRefCustomModifiers: default,
                        parameterRefKinds: ImmutableArray<RefKind>.Empty,
                        compilation: comp));
        }
    }
}