File: CodeGen\EventTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit\Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit.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.
 
#nullable disable
 
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit;
using Microsoft.CodeAnalysis.Text;
using Xunit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
    public class EventTests : EmitMetadataTestBase
    {
        #region Metadata and IL
 
        [Fact]
        public void InstanceCustomEvent()
        {
            var text = @"
class C
{
    public event System.Action E
    {
        add { value(); }
        remove { value(); }
    }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: false, isFieldLike: false),
                expectedSignatures: new[]
                {
                    Signature("C", "E", ".event System.Action E"),
                    Signature("C", "add_E", ".method public hidebysig specialname instance System.Void add_E(System.Action value) cil managed"),
                    Signature("C", "remove_E", ".method public hidebysig specialname instance System.Void remove_E(System.Action value) cil managed"),
                });
 
            var accessorBody = @"
{
  // Code size        7 (0x7)
  .maxstack  1
  IL_0000:  ldarg.1
  IL_0001:  callvirt   ""void System.Action.Invoke()""
  IL_0006:  ret
}";
            compVerifier.VerifyIL("C.E.add", accessorBody);
            compVerifier.VerifyIL("C.E.remove", accessorBody);
        }
 
        [Fact]
        public void StaticCustomEvent()
        {
            var text = @"
class C
{
    public static event System.Action E
    {
        add { value(); }
        remove { value(); }
    }
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: true, isFieldLike: false),
                expectedSignatures: new[]
                {
                    Signature("C", "E", ".event System.Action E"),
                    Signature("C", "add_E", ".method public hidebysig specialname static System.Void add_E(System.Action value) cil managed"),
                    Signature("C", "remove_E", ".method public hidebysig specialname static System.Void remove_E(System.Action value) cil managed")
                });
 
            var accessorBody = @"
{
  // Code size        7 (0x7)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  callvirt   ""void System.Action.Invoke()""
  IL_0006:  ret
}";
            compVerifier.VerifyIL("C.E.add", accessorBody);
            compVerifier.VerifyIL("C.E.remove", accessorBody);
        }
 
        [Fact]
        public void InstanceFieldLikeEvent()
        {
            var text = @"
class C
{
    public event System.Action E;
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: false, isFieldLike: true),
                expectedSignatures: new[]
                {
                    Signature("C", "E", ".event System.Action E"),
                    Signature("C", "add_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname instance System.Void add_E(System.Action value) cil managed"),
                    Signature("C", "remove_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname instance System.Void remove_E(System.Action value) cil managed")
                });
 
            var accessorBodyFormat = @"
{{
  // Code size       41 (0x29)
  .maxstack  3
  .locals init (System.Action V_0,
  System.Action V_1,
  System.Action V_2)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      ""System.Action C.E""
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  stloc.1
  IL_0009:  ldloc.1
  IL_000a:  ldarg.1
  IL_000b:  call       ""System.Delegate System.Delegate.{0}(System.Delegate, System.Delegate)""
  IL_0010:  castclass  ""System.Action""
  IL_0015:  stloc.2
  IL_0016:  ldarg.0
  IL_0017:  ldflda     ""System.Action C.E""
  IL_001c:  ldloc.2
  IL_001d:  ldloc.1
  IL_001e:  call       ""System.Action System.Threading.Interlocked.CompareExchange<System.Action>(ref System.Action, System.Action, System.Action)""
  IL_0023:  stloc.0
  IL_0024:  ldloc.0
  IL_0025:  ldloc.1
  IL_0026:  bne.un.s   IL_0007
  IL_0028:  ret
}}";
 
            // NOTE: Dev10 used slightly different loop condition code
            //  IL_0023:  stloc.0
            //  IL_0024:  ldloc.0
            //  IL_0025:  ldloc.1
            //  IL_0026:  ceq
            //  IL_0028:  ldc.i4.0
            //  IL_0029:  ceq
            //  IL_002b:  stloc.3
            //  IL_002c:  ldloc.3
            //  IL_002d:  brtrue.s   IL_0007
            //  IL_002f:  ret
 
            compVerifier.VerifyIL("C.E.add", string.Format(accessorBodyFormat, "Combine"));
            compVerifier.VerifyIL("C.E.remove", string.Format(accessorBodyFormat, "Remove"));
        }
 
        [Fact]
        public void StaticFieldLikeEvent()
        {
            var text = @"
class C
{
    public static event System.Action E;
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: true, isFieldLike: true),
                expectedSignatures: new[]
                {
                    Signature("C", "E", ".event System.Action E"),
                    Signature("C", "add_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname static System.Void add_E(System.Action value) cil managed"),
                    Signature("C", "remove_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig specialname static System.Void remove_E(System.Action value) cil managed")
                });
 
            var accessorBodyFormat = @"
{{
  // Code size       39 (0x27)
  .maxstack  3
  .locals init (System.Action V_0,
  System.Action V_1,
  System.Action V_2)
  IL_0000:  ldsfld     ""System.Action C.E""
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  stloc.1
  IL_0008:  ldloc.1
  IL_0009:  ldarg.0
  IL_000a:  call       ""System.Delegate System.Delegate.{0}(System.Delegate, System.Delegate)""
  IL_000f:  castclass  ""System.Action""
  IL_0014:  stloc.2
  IL_0015:  ldsflda    ""System.Action C.E""
  IL_001a:  ldloc.2
  IL_001b:  ldloc.1
  IL_001c:  call       ""System.Action System.Threading.Interlocked.CompareExchange<System.Action>(ref System.Action, System.Action, System.Action)""
  IL_0021:  stloc.0
  IL_0022:  ldloc.0
  IL_0023:  ldloc.1
  IL_0024:  bne.un.s   IL_0006
  IL_0026:  ret
}}";
            // NOTE: Dev10 used slightly different loop condition code (as in InstanceFieldLikeEvent) 
 
            compVerifier.VerifyIL("C.E.add", string.Format(accessorBodyFormat, "Combine"));
            compVerifier.VerifyIL("C.E.remove", string.Format(accessorBodyFormat, "Remove"));
        }
 
        // NOTE: assumes there's an event E in a type C.
        private static void ValidateEvent(ModuleSymbol module, bool isFromSource, bool isStatic, bool isFieldLike)
        {
            var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
            var @event = @class.GetMember<EventSymbol>("E");
 
            Assert.Equal(SymbolKind.Event, @event.Kind);
            Assert.Equal(Accessibility.Public, @event.DeclaredAccessibility);
            Assert.Equal(isStatic, @event.IsStatic);
            Assert.False(@event.MustCallMethodsDirectly);
 
            var addMethod = @event.AddMethod;
            Assert.Equal(MethodKind.EventAdd, addMethod.MethodKind);
            Assert.Equal("void C.E.add", addMethod.ToTestDisplayString());
            Assert.True((addMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0);
            addMethod.CheckAccessorShape(@event);
 
            var removeMethod = @event.RemoveMethod;
            Assert.Equal(MethodKind.EventRemove, removeMethod.MethodKind);
            Assert.Equal("void C.E.remove", removeMethod.ToTestDisplayString());
            Assert.True((removeMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0);
            removeMethod.CheckAccessorShape(@event);
 
            // Whether or not the event was field-like in source, it will look custom when loaded from metadata.
            if (isFieldLike && isFromSource)
            {
                Assert.True(@event.HasAssociatedField);
                var associatedField = @event.AssociatedField;
                Assert.Equal(SymbolKind.Field, associatedField.Kind);
                Assert.Equal(Accessibility.Private, associatedField.DeclaredAccessibility);
                Assert.Equal(isStatic, associatedField.IsStatic);
                Assert.Equal(@event.Type, associatedField.Type);
            }
            else
            {
                Assert.False(@event.HasAssociatedField);
                Assert.Null(@event.AssociatedField);
            }
        }
 
        [Fact]
        public void EventOperations()
        {
            var text = @"
class C
{
    public event System.Action E;
    public event System.Action F { add { } remove { } }
 
    void M(ref System.Action a)
    {
        E = a;
        a = E;
        M(ref E);
        E += a;
        E -= a;
 
        F += a;
        F -= a;
        F += E;
        F -= E;
    }
}
";
            CompileAndVerify(text).VerifyIL("C.M", @"
{
  // Code size       85 (0x55)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  ldind.ref
  IL_0003:  stfld      ""System.Action C.E""
  IL_0008:  ldarg.1
  IL_0009:  ldarg.0
  IL_000a:  ldfld      ""System.Action C.E""
  IL_000f:  stind.ref
  IL_0010:  ldarg.0
  IL_0011:  ldarg.0
  IL_0012:  ldflda     ""System.Action C.E""
  IL_0017:  call       ""void C.M(ref System.Action)""
  IL_001c:  ldarg.0
  IL_001d:  ldarg.1
  IL_001e:  ldind.ref
  IL_001f:  call       ""void C.E.add""
  IL_0024:  ldarg.0
  IL_0025:  ldarg.1
  IL_0026:  ldind.ref
  IL_0027:  call       ""void C.E.remove""
  IL_002c:  ldarg.0
  IL_002d:  ldarg.1
  IL_002e:  ldind.ref
  IL_002f:  call       ""void C.F.add""
  IL_0034:  ldarg.0
  IL_0035:  ldarg.1
  IL_0036:  ldind.ref
  IL_0037:  call       ""void C.F.remove""
  IL_003c:  ldarg.0
  IL_003d:  ldarg.0
  IL_003e:  ldfld      ""System.Action C.E""
  IL_0043:  call       ""void C.F.add""
  IL_0048:  ldarg.0
  IL_0049:  ldarg.0
  IL_004a:  ldfld      ""System.Action C.E""
  IL_004f:  call       ""void C.F.remove""
  IL_0054:  ret
}");
        }
 
        [Fact]
        public void StaticEventOperations()
        {
            var text = @"
class C
{
    public static event System.Action E;
    public static event System.Action F { add { } remove { } }
 
    void M(ref System.Action a)
    {
        E = a;
        a = E;
        M(ref E);
        E += a;
        E -= a;
 
        F += a;
        F -= a;
        F += E;
        F -= E;
    }
}
";
            CompileAndVerify(text).VerifyIL("C.M", @"
{
  // Code size       74 (0x4a)
  .maxstack  2
  IL_0000:  ldarg.1
  IL_0001:  ldind.ref
  IL_0002:  stsfld     ""System.Action C.E""
  IL_0007:  ldarg.1
  IL_0008:  ldsfld     ""System.Action C.E""
  IL_000d:  stind.ref
  IL_000e:  ldarg.0
  IL_000f:  ldsflda    ""System.Action C.E""
  IL_0014:  call       ""void C.M(ref System.Action)""
  IL_0019:  ldarg.1
  IL_001a:  ldind.ref
  IL_001b:  call       ""void C.E.add""
  IL_0020:  ldarg.1
  IL_0021:  ldind.ref
  IL_0022:  call       ""void C.E.remove""
  IL_0027:  ldarg.1
  IL_0028:  ldind.ref
  IL_0029:  call       ""void C.F.add""
  IL_002e:  ldarg.1
  IL_002f:  ldind.ref
  IL_0030:  call       ""void C.F.remove""
  IL_0035:  ldsfld     ""System.Action C.E""
  IL_003a:  call       ""void C.F.add""
  IL_003f:  ldsfld     ""System.Action C.E""
  IL_0044:  call       ""void C.F.remove""
  IL_0049:  ret
}");
        }
 
        [Fact]
        public void EventAccess()
        {
            var text = @"
class C
{
    public event System.Action E;
    public event System.Action F { add { } remove { } }
    public static event System.Action G;
    public static event System.Action H { add { } remove { } }
}
 
class D
{
    void M(C c, System.Action a)
    {
        c.E += a;
        c.E -= a;
        c.F += a;
        c.F -= a;
        C.G += a;
        C.G -= a;
        C.H += a;
        C.H -= a;
    }
}
";
            CompileAndVerify(text).VerifyIL("D.M", @"
{
  // Code size       53 (0x35)
  .maxstack  2
  IL_0000:  ldarg.1
  IL_0001:  ldarg.2
  IL_0002:  callvirt   ""void C.E.add""
  IL_0007:  ldarg.1
  IL_0008:  ldarg.2
  IL_0009:  callvirt   ""void C.E.remove""
  IL_000e:  ldarg.1
  IL_000f:  ldarg.2
  IL_0010:  callvirt   ""void C.F.add""
  IL_0015:  ldarg.1
  IL_0016:  ldarg.2
  IL_0017:  callvirt   ""void C.F.remove""
  IL_001c:  ldarg.2
  IL_001d:  call       ""void C.G.add""
  IL_0022:  ldarg.2
  IL_0023:  call       ""void C.G.remove""
  IL_0028:  ldarg.2
  IL_0029:  call       ""void C.H.add""
  IL_002e:  ldarg.2
  IL_002f:  call       ""void C.H.remove""
  IL_0034:  ret
}");
        }
 
        // Regresses IsMetadataVirtual issue (no associated bug).
        [Fact]
        public void InterfaceEvent()
        {
            var text = @"
interface C
{
    event System.Action E;
}
";
            var compVerifier = CompileAndVerify(text,
                symbolValidator: module => ValidateEvent(module, isFromSource: false, isStatic: false, isFieldLike: true),
                expectedSignatures: new[]
                {
                    Signature("C", "E", ".event System.Action E"),
                    Signature("C", "add_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig newslot specialname abstract virtual instance System.Void add_E(System.Action value) cil managed"),
                    Signature("C", "remove_E", ".method [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public hidebysig newslot specialname abstract virtual instance System.Void remove_E(System.Action value) cil managed")
                });
        }
 
        #endregion
 
        #region Execution
 
        [Fact]
        public void CustomEventExecution()
        {
            var text = @"
using System;
 
class C
{
    Func<string> e;
    event Func<string> E
    {
        add
        {
            Console.Write(""Adding "");
            value();
            Console.WriteLine();
            e += value;
        }
        remove
        {
            Console.Write(""Removing "");
            value();
            Console.WriteLine();
            e -= value;
        }
    }
 
    void Fire()
    {
        if (e != null)
        {
            Console.Write(""Invoking "");
            e();
            Console.WriteLine();
        }
        else
        {
            Console.WriteLine(""No handlers"");
        }
    }
 
    static void Main()
    {
        C c = new C();
        Func<string> handler1 = () =>
        {
            Console.Write(""Handler1 "");
            return ""Handler1"";
        };
        Func<string> handler2 = () =>
        {
            Console.Write(""Handler2 "");
            return ""Handler2"";
        };
 
        c.Fire();
        c.E += handler1;
        c.Fire();
        c.E += handler2;
        c.Fire();
        c.E -= handler1;
        c.Fire();
        c.E -= handler2;
        c.Fire();
    }
}";
 
            CompileAndVerify(text, expectedOutput: @"
No handlers
Adding Handler1 
Invoking Handler1 
Adding Handler2 
Invoking Handler1 Handler2 
Removing Handler1 
Invoking Handler2 
Removing Handler2 
No handlers
");
        }
 
        [Fact]
        public void FieldLikeEventExecution()
        {
            var text = @"
using System;
 
class C
{
    event Func<string> E;
 
    void Fire()
    {
        if (E != null)
        {
            Console.Write(""Invoking "");
            E();
            Console.WriteLine();
        }
        else
        {
            Console.WriteLine(""No handlers"");
        }
    }
 
    static void Main()
    {
        C c = new C();
        Func<string> handler1 = () =>
        {
            Console.Write(""Handler1 "");
            return ""Handler1"";
        };
        Func<string> handler2 = () =>
        {
            Console.Write(""Handler2 "");
            return ""Handler2"";
        };
 
        c.Fire();
        c.E += handler1;
        c.Fire();
        c.E += handler2;
        c.Fire();
        c.E -= handler1;
        c.Fire();
        c.E -= handler2;
        c.Fire();
    }
}";
 
            CompileAndVerify(text, expectedOutput: @"
No handlers
Invoking Handler1 
Invoking Handler1 Handler2 
Invoking Handler2 
No handlers
");
        }
 
        [Fact]
        public void VirtualRaiseAccessor()
        {
            var csharpSource = @"
using System;
 
static class Program
{
    static void Main()
    {
        C c = new C();
        c.Fire(); //Prints ""VirtualEventWithRaise Raise"" since raise_e isn't overridden
 
        D d = new D();
        d.Fire(); //Prints ""D raise"" since raise_e is overridden (regardless of event)
    }
}
 
class C : VirtualEventWithRaise
{
    public override event Action e = () => Console.WriteLine(""C Handler"");
}
 
class D : C
{
    public override void raise_e(object sender, object e)
    {
        Console.WriteLine(""D Raise"");
    }
}
";
 
            var ilAssemblyReference = TestReferences.SymbolsTests.Events;
            var compilation = CreateCompilation(csharpSource, new MetadataReference[] { ilAssemblyReference }, TestOptions.ReleaseExe);
            CompileAndVerify(compilation, expectedOutput: @"
VirtualEventWithRaise Raise
D Raise
");
        }
 
        #endregion Execution
 
        [Fact]
        public void MissingCompareExchange_01()
        {
            var source1 =
@"namespace System
{
    public class Object { }
    public struct Void { }
    public class ValueType { }
    public struct Boolean { }
    public abstract class Delegate { }
    public abstract class MulticastDelegate : Delegate { }
    public struct IntPtr { private IntPtr m_value; IntPtr Use(IntPtr b) { m_value = b; return m_value; } }
}
";
 
            var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute();
            var compilation1 = CreateEmptyCompilation(source1, assemblyName: GetUniqueName(), parseOptions: parseOptions);
            var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
            var source2 =
@"
 
public delegate void E1();
 
class C
{
    public event E1 e;
 
    public static void Main()
    {
        var v = new C();
        v.e += Main;
    }
}
";
            var compilation2 = CreateEmptyCompilation(source2, new[] { reference1 }, parseOptions: parseOptions);
            compilation2.VerifyDiagnostics(
                // (7,21): warning CS0067: The event 'C.e' is never used
                //     public event E1 e;
                Diagnostic(ErrorCode.WRN_UnreferencedEvent, "e").WithArguments("C.e")
            );
 
            compilation2.Emit(new System.IO.MemoryStream()).Diagnostics.Verify(
    // (7,21): error CS0656: Missing compiler required member 'System.Delegate.Combine'
    //     public event E1 e;
    Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e").WithArguments("System.Delegate", "Combine").WithLocation(7, 21),
    // (7,21): error CS0656: Missing compiler required member 'System.Delegate.Remove'
    //     public event E1 e;
    Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "e").WithArguments("System.Delegate", "Remove").WithLocation(7, 21),
    // (7,21): warning CS0067: The event 'C.e' is never used
    //     public event E1 e;
    Diagnostic(ErrorCode.WRN_UnreferencedEvent, "e").WithArguments("C.e").WithLocation(7, 21)
                );
        }
 
        [Fact, WorkItem(1027568, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1027568"), WorkItem(528573, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528573")]
        public void MissingCompareExchange_02()
        {
            var source =
@"
 
public delegate void E1();
 
class C
{
    public event E1 e;
 
    public static void Main()
    {
        var v = new C();
        System.Console.Write(v.e == null);
        v.e += Main;
        System.Console.Write(v.e == null);
        v.e -= Main;
        System.Console.Write(v.e == null);
    }
}
";
            var compilation = CreateCompilation(source, options: TestOptions.DebugExe);
 
            compilation.MakeMemberMissing(WellKnownMember.System_Threading_Interlocked__CompareExchange_T);
 
            var verifier = CompileAndVerify(compilation,
                                            expectedOutput: "TrueFalseTrue",
                                            symbolValidator: module =>
                                                                {
                                                                    var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
                                                                    var @event = @class.GetMember<EventSymbol>("e");
 
                                                                    var addMethod = @event.AddMethod;
                                                                    Assert.False((addMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0);
 
                                                                    var removeMethod = @event.RemoveMethod;
                                                                    Assert.False((removeMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0);
                                                                }).VerifyDiagnostics();
 
            verifier.VerifyIL("C.e.add", @"
{
  // Code size       24 (0x18)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  ldarg.0
  IL_0002:  ldfld      ""E1 C.e""
  IL_0007:  ldarg.1
  IL_0008:  call       ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
  IL_000d:  castclass  ""E1""
  IL_0012:  stfld      ""E1 C.e""
  IL_0017:  ret
}
");
 
            verifier.VerifyIL("C.e.remove", @"
{
  // Code size       24 (0x18)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  ldarg.0
  IL_0002:  ldfld      ""E1 C.e""
  IL_0007:  ldarg.1
  IL_0008:  call       ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)""
  IL_000d:  castclass  ""E1""
  IL_0012:  stfld      ""E1 C.e""
  IL_0017:  ret
}
");
        }
 
        [Fact, WorkItem(1027568, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1027568"), WorkItem(528573, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528573")]
        public void MissingCompareExchange_03()
        {
            var source =
@"
 
public delegate void E1();
 
struct C
{
    public event E1 e;
 
    public static void Main()
    {
        var v = new C();
        System.Console.Write(v.e == null);
        v.e += Main;
        System.Console.Write(v.e == null);
        v.e -= Main;
        System.Console.Write(v.e == null);
    }
}
";
            var compilation = CreateCompilation(source, options: TestOptions.DebugExe);
 
            compilation.MakeMemberMissing(WellKnownMember.System_Threading_Interlocked__CompareExchange_T);
 
            var verifier = CompileAndVerify(compilation,
                                            expectedOutput: "TrueFalseTrue",
                                            symbolValidator: module =>
                                            {
                                                var @class = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
                                                var @event = @class.GetMember<EventSymbol>("e");
 
                                                var addMethod = @event.AddMethod;
                                                Assert.True((addMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0);
 
                                                var removeMethod = @event.RemoveMethod;
                                                Assert.True((removeMethod.ImplementationAttributes & System.Reflection.MethodImplAttributes.Synchronized) == 0);
                                            }).VerifyDiagnostics();
 
            verifier.VerifyIL("C.e.add", @"
{
  // Code size       24 (0x18)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  ldarg.0
  IL_0002:  ldfld      ""E1 C.e""
  IL_0007:  ldarg.1
  IL_0008:  call       ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
  IL_000d:  castclass  ""E1""
  IL_0012:  stfld      ""E1 C.e""
  IL_0017:  ret
}
");
 
            verifier.VerifyIL("C.e.remove", @"
{
  // Code size       24 (0x18)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  ldarg.0
  IL_0002:  ldfld      ""E1 C.e""
  IL_0007:  ldarg.1
  IL_0008:  call       ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)""
  IL_000d:  castclass  ""E1""
  IL_0012:  stfld      ""E1 C.e""
  IL_0017:  ret
}
");
        }
 
        [Fact, WorkItem(14438, "https://github.com/dotnet/roslyn/issues/14438")]
        [CompilerTrait(CompilerFeature.ExpressionBody)]
        public void ExpressionBodedEvent()
        {
            var source = @"
class C
{
    public int x;
    public event System.Action E
    {
        add => x = 1;
        remove => x = 0;
    }
}";
            var compilation = CreateCompilation(source, options: TestOptions.DebugDll);
            var verifier = CompileAndVerify(compilation);
            verifier.VerifyIL("C.E.add", @"
{
  // Code size        8 (0x8)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.1
  IL_0002:  stfld      ""int C.x""
  IL_0007:  ret
}
");
            verifier.VerifyIL("C.E.remove", @"
{
  // Code size        8 (0x8)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.0
  IL_0002:  stfld      ""int C.x""
  IL_0007:  ret
}
");
        }
    }
}