File: Semantics\ReadOnlyStructsTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Semantic\Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Semantic.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.Retargeting;
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.Semantics
{
    [CompilerTrait(CompilerFeature.ReadOnlyReferences)]
    public class ReadOnlyStructsTests : CompilingTestBase
    {
        [Fact()]
        public void WriteableInstanceAutoPropsInRoStructs()
        {
            var text = @"
public readonly struct A
{
    // ok   - no state
    int ro => 5;
 
    // ok   - ro state
    int ro1 {get;}
 
    // error
    int rw {get; set;}
 
    // ok    - static
    static int rws {get; set;}
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (11,9): error CS8341: Auto-implemented instance properties in readonly structs must be readonly.
                //     int rw {get; set;}
                Diagnostic(ErrorCode.ERR_AutoPropsInRoStruct, "rw").WithLocation(11, 9)
    );
        }
 
        [Fact()]
        public void WriteableInstanceFieldsInRoStructs()
        {
            var text = @"
public readonly struct A
{
    // ok
    public static int s;
 
    // ok
    public readonly int ro;
 
    // error
    int x;    
 
    void AssignField()
    {
        // error
        this = default;
        // error
        this.x = 1;
 
        A a = default;
        // OK
        a.x = 2;
    }
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (11,9): error CS8340: Instance fields of readonly structs must be readonly.
                //     int x;    
                Diagnostic(ErrorCode.ERR_FieldsInRoStruct, "x").WithLocation(11, 9),
                // (16,9): error CS1604: Cannot assign to 'this' because it is read-only
                //         this = default;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(16, 9),
                // (18,9): error CS1604: Cannot assign to 'this.x' because it is read-only
                //         this.x = 1;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this.x").WithArguments("this.x").WithLocation(18, 9));
        }
 
        [Fact()]
        public void EventsInRoStructs()
        {
            var text = @"
using System;
 
public readonly struct A : I1
{
    //error
    public event System.Action e;
 
    //error
    public event Action ei1;
 
    //ok
    public static event Action es;
 
    A(int arg)
    {
        // ok
        e = () => { };
        ei1 = () => { };
        es = () => { };
        
        // ok
        M1(ref e);
    }
 
    //ok
    event Action I1.ei2
    {
        add
        {
            throw new NotImplementedException();
        }
 
        remove
        {
            throw new NotImplementedException();
        }
    }
 
    void AssignEvent()
    {
        // error
        e = () => { };
 
        // error
        M1(ref e);
    }
 
    static void M1(ref System.Action arg)
    {
    }
}
 
interface I1
{
    event System.Action ei1;
    event System.Action ei2;
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (7,32): error CS8342: Field-like events are not allowed in readonly structs.
                //     public event System.Action e;
                Diagnostic(ErrorCode.ERR_FieldlikeEventsInRoStruct, "e").WithLocation(7, 32),
                // (10,25): error CS8342: Field-like events are not allowed in readonly structs.
                //     public event Action ei1;
                Diagnostic(ErrorCode.ERR_FieldlikeEventsInRoStruct, "ei1").WithLocation(10, 25),
                // (43,9): error CS1604: Cannot assign to 'e' because it is read-only
                //         e = () => { };
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "e").WithArguments("e").WithLocation(43, 9),
                // (46,16): error CS1605: Cannot use 'e' as a ref or out value because it is read-only
                //         M1(ref e);
                Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "e").WithArguments("e").WithLocation(46, 16)
            );
        }
 
        [Fact]
        public void WriteableInstanceFields_ReadOnlyMethod()
        {
            var text = @"
public struct A
{
    public static int s;
 
    public int x;
 
    readonly void AssignField()
    {
        // error
        this = default;
        // error
        this.x = 1;
 
        A a = default;
        // OK
        a.x = 3;
        // OK
        s = 5;
    }
}
";
            CreateCompilation(text).VerifyDiagnostics(
                // (11,9): error CS1604: Cannot assign to 'this' because it is read-only
                //         this = default;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this").WithArguments("this").WithLocation(11, 9),
                // (13,9): error CS1604: Cannot assign to 'this.x' because it is read-only
                //         this.x = 1;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this.x").WithArguments("this.x").WithLocation(13, 9));
        }
 
        [Fact]
        public void ReadOnlyStruct_PassThisByRef()
        {
            var csharp = @"
public readonly struct S
{
    public static void M1(ref S s) {}
    public static void M2(in S s) {}
 
    public void M3()
    {
        M1(ref this); // error
        M2(in this); // ok
    }
 
    public readonly void M4()
    {
        M1(ref this); // error
        M2(in this); // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (9,16): error CS1605: Cannot use 'this' as a ref or out value because it is read-only
                //         M1(ref this); // error
                Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(9, 16),
                // (15,16): error CS1605: Cannot use 'this' as a ref or out value because it is read-only
                //         M1(ref this); // error
                Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(15, 16));
        }
 
        [Fact]
        public void ReadOnlyMethod_PassThisByRef()
        {
            var csharp = @"
public struct S
{
    public static void M1(ref S s) {}
    public static void M2(in S s) {}
 
    public void M3()
    {
        M1(ref this); // ok
        M2(in this); // ok
    }
 
    public readonly void M4()
    {
        M1(ref this); // error
        M2(in this); // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (15,16): error CS1605: Cannot use 'this' as a ref or out value because it is read-only
                //         M1(ref this); // error
                Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(15, 16));
        }
 
        [Fact]
        public void ReadOnlyMethod_RefLocal()
        {
            var csharp = @"
public struct S
{
    public void M1()
    {
        ref S s1 = ref this; // ok
        ref readonly S s2 = ref this; // ok
    }
 
    public readonly void M2()
    {
        ref S s1 = ref this; // error
        ref readonly S s2 = ref this; // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (12,24): error CS1605: Cannot use 'this' as a ref or out value because it is read-only
                //         ref S s1 = ref this; // error
                Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "this").WithArguments("this").WithLocation(12, 24));
        }
 
        [Fact]
        public void ReadOnlyMethod_PassFieldByRef()
        {
            var csharp = @"
public struct S
{
    public static int f1;
    public int f2;
 
    public static void M1(ref int s) {}
    public static void M2(in int s) {}
 
    public void M3()
    {
        M1(ref f1); // ok
        M1(ref f2); // ok
        M2(in f1); // ok
        M2(in f2); // ok
    }
 
    public readonly void M4()
    {
        M1(ref f1); // ok
        M1(ref f2); // error
        M2(in f1); // ok
        M2(in f2); // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (21,16): error CS1605: Cannot use 'f2' as a ref or out value because it is read-only
                //         M1(ref f2); // error
                Diagnostic(ErrorCode.ERR_RefReadonlyLocal, "f2").WithArguments("f2").WithLocation(21, 16));
        }
 
        [Fact]
        public void ReadOnlyMethod_CallStaticMethod()
        {
            var csharp = @"
public struct S
{
    public static int i;
    public readonly int M1() => M2() + 1;
    public static int M2() => i;
}
";
            var comp = CompileAndVerify(csharp);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyMethod_Override_AssignThis()
        {
            var csharp = @"
public struct S
{
    public int i;
 
    // error
    public readonly override string ToString() => (i++).ToString();
    public readonly override int GetHashCode() => (i++).GetHashCode();
    public readonly override bool Equals(object o) => (i++).Equals(o);
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (7,52): error CS1604: Cannot assign to 'i' because it is read-only
                //     public readonly override string ToString() => (i++).ToString();
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(7, 52),
                // (8,52): error CS1604: Cannot assign to 'i' because it is read-only
                //     public readonly override int GetHashCode() => (i++).GetHashCode();
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 52),
                // (9,56): error CS1604: Cannot assign to 'i' because it is read-only
                //     public readonly override bool Equals(object o) => (i++).Equals(o);
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(9, 56));
        }
 
        [Fact]
        public void ReadOnlyMethod_Partial_01()
        {
            var csharp = @"
public partial struct S
{
    public int i;
    readonly partial void M();
}
 
public partial struct S
{
    readonly partial void M()
    {
        i++;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (12,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i++;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(12, 9));
 
            var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M");
            Assert.True(method.IsDeclaredReadOnly);
            Assert.True(method.IsEffectivelyReadOnly);
        }
 
        [Fact]
        public void ReadOnlyMethod_Partial_02()
        {
            var csharp = @"
public partial struct S
{
    public int i;
    partial void M();
}
 
public partial struct S
{
    readonly partial void M()
    {
        i++;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (10,27): error CS8662: Both partial member declarations must be readonly or neither may be readonly
                //     readonly partial void M()
                Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "M").WithLocation(10, 27),
                // (12,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i++;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(12, 9));
 
            var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M");
            // Symbol APIs always return the declaration part of the partial method.
            Assert.False(method.IsDeclaredReadOnly);
            Assert.False(method.IsEffectivelyReadOnly);
        }
 
        [Fact]
        public void ReadOnlyMethod_Partial_03()
        {
            var csharp = @"
public partial struct S
{
    public int i;
    readonly partial void M();
}
 
public partial struct S
{
    partial void M()
    {
        i++;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (10,18): error CS8662: Both partial member declarations must be readonly or neither may be readonly
                //     partial void M()
                Diagnostic(ErrorCode.ERR_PartialMemberReadOnlyDifference, "M").WithLocation(10, 18));
 
            var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M");
            // Symbol APIs always return the declaration part of the partial method.
            Assert.True(method.IsDeclaredReadOnly);
            Assert.True(method.IsEffectivelyReadOnly);
        }
 
        [Fact]
        public void ReadOnlyMethod_ExplicitInterfaceImplementation()
        {
            var csharp = @"
public interface I
{
    void M();
}
 
public struct S : I
{
    int i;
    readonly void I.M()
    {
        i = 0;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (12,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i = 0;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(12, 9));
        }
 
        [Fact]
        public void ReadOnlyMethod_GetPinnableReference()
        {
            var csharp = @"
public unsafe struct S1
{
    static int i = 0;
    ref int GetPinnableReference() => ref i;
    void M1()
    {
        fixed (int *i = this) {} // ok
    }
    readonly void M2()
    {
        fixed (int *i = this) {} // warn
    }
}
 
public unsafe struct S2
{
    static int i = 0;
    readonly ref int GetPinnableReference() => ref i;
    void M1()
    {
        fixed (int *i = this) {} // ok
    }
    readonly void M2()
    {
        fixed (int *i = this) {} // ok
    }
}
";
            var comp = CreateCompilation(csharp, options: TestOptions.UnsafeReleaseDll);
            comp.VerifyDiagnostics(
                // (12,25): warning CS8655: Call to non-readonly member 'S1.GetPinnableReference()' from a 'readonly' member results in an implicit copy of 'this'.
                //         fixed (int *i = this) {} // warn
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetPinnableReference()", "this").WithLocation(12, 25));
        }
 
        [Fact]
        public void ReadOnlyMethod_Iterator()
        {
            var csharp = @"
using System.Collections.Generic;
 
public struct S
{
    public int i;
    public readonly IEnumerable<int> M1()
    {
        yield return i;
        yield return i+1;
    }
 
    public readonly IEnumerable<int> M2()
    {
        yield return i;
        i++;
        yield return i;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (16,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i++;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(16, 9));
        }
 
        [Fact]
        public void ReadOnlyMethod_Async()
        {
            var csharp = @"
using System.Threading.Tasks;
 
public struct S
{
    public int i;
    public readonly async Task<int> M1()
    {
        await Task.Delay(1);
        return i;
    }
 
    public readonly async Task<int> M2()
    {
        await Task.Delay(1);
        i++;
        await Task.Delay(1);
        return i;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (16,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i++;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(16, 9));
        }
 
        [Fact]
        public void ReadOnlyAccessor_CallNormalMethod()
        {
            var csharp = @"
public struct S
{
    public int i;
 
    public readonly int P
    {
        get
        {
            // should create local copy
            M();
            System.Console.Write(i);
 
            // explicit local copy, no warning
            var copy = this;
            copy.M();
            System.Console.Write(copy.i);
 
            return i;
        }
    }
 
    void M()
    {
        i = 23;
    }
 
    static void Main()
    {
        var s = new S { i = 1 };
        _ = s.P;
    }
}
";
 
            var verifier = CompileAndVerify(csharp, expectedOutput: "123");
            verifier.VerifyDiagnostics(
                // (11,13): warning CS8655: Call to non-readonly member 'S.M()' from a 'readonly' member results in an implicit copy of 'this'.
                //             M();
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "M").WithArguments("S.M()", "this").WithLocation(11, 13));
        }
 
        [Fact]
        public void ReadOnlyAccessor_CallNormalGetAccessor()
        {
            var csharp = @"
public struct S
{
    public int i;
 
    public readonly int P1
    {
        get
        {
            // should create local copy
            _ = P2; // warning
            System.Console.Write(i);
 
            // explicit local copy, no warning
            var copy = this;
            _ = copy.P2; // ok
            System.Console.Write(copy.i);
 
            return i;
        }
    }
 
    int P2 => i = 23;
 
    static void Main()
    {
        var s = new S { i = 1 };
        _ = s.P1;
    }
}
";
 
            var verifier = CompileAndVerify(csharp, expectedOutput: "123");
            verifier.VerifyDiagnostics(
                // (11,17): warning CS8655: Call to non-readonly member 'S.P2.get' from a 'readonly' member results in an implicit copy of 'this'.
                //             _ = P2; // warning
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "P2").WithArguments("S.P2.get", "this").WithLocation(11, 17));
        }
 
        [Fact]
        public void ReadOnlyMethod_CallSetAccessor()
        {
            var csharp = @"
public class C
{
    public int P { get; set; }
}
 
public struct S
{
    C c;
    public S(C c)
    {
        this.c = c;
        P2 = 0;
    }
 
    static int P1 { get; set; }
    int P2 { get; set; }
    readonly int P3
    {
        get => 42;
        set {}
    }
 
    public readonly void M()
    {
        P1 = 1; // ok
        P2 = 2; // error
        P3 = 2; // ok
        c.P = 42; // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (27,9): error CS1604: Cannot assign to 'P2' because it is read-only
                //         P2 = 2; // error
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "P2").WithArguments("P2").WithLocation(27, 9));
        }
 
        [Fact]
        public void ReadOnlyMethod_IncrementOperator()
        {
            var csharp = @"
public struct S
{
    public int i;
    public readonly void M()
    {
        i++;
        i--;
        ++i;
        --i;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (7,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i++;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(7, 9),
                // (8,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i--;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 9),
                // (9,11): error CS1604: Cannot assign to 'i' because it is read-only
                //         ++i;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(9, 11),
                // (10,11): error CS1604: Cannot assign to 'i' because it is read-only
                //         --i;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(10, 11));
        }
 
        [Fact]
        public void ReadOnlyMethod_CompoundAssignmentOperator()
        {
            var csharp = @"
public struct S
{
    public int i;
    public readonly void M()
    {
        i += 1;
        i -= 1;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (7,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i += 1;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(7, 9),
                // (8,9): error CS1604: Cannot assign to 'i' because it is read-only
                //         i -= 1;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 9));
        }
 
        [Fact]
        public void ReadOnlyMethod_EventAssignment()
        {
            var csharp = @"
using System;
 
public struct S
{
    public readonly void M()
    {
        Console.WriteLine(E is null);
        // should create local copy
        E += () => {}; // warning
        Console.WriteLine(E is null);
        E -= () => {}; // warning
 
        // explicit local copy, no warning
        var copy = this;
        copy.E += () => {};
        Console.WriteLine(copy.E is null);
    }
 
    public event Action E;
 
    static void Main()
    {
        var s = new S();
        s.M();
    }
}
";
 
            var verifier = CompileAndVerify(csharp, expectedOutput:
@"True
True
False");
            verifier.VerifyDiagnostics(
                // (10,9): warning CS8656: Call to non-readonly member 'S.E.add' from a 'readonly' member results in an implicit copy of 'this'.
                //         E += () => {}; // warning
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.add", "this").WithLocation(10, 9),
                // (12,9): warning CS8656: Call to non-readonly member 'S.E.remove' from a 'readonly' member results in an implicit copy of 'this'.
                //         E -= () => {}; // warning
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.remove", "this").WithLocation(12, 9));
        }
 
        [Fact]
        public void ReadOnlyStruct_EventAssignment()
        {
            var csharp = @"
using System;
 
public struct S
{
    public void M()
    {
        E += () => {};
        E -= () => {};
    }
 
    public event Action E { add {} remove {} }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyMethod_ReadOnlyEventAssignment()
        {
            var csharp = @"
using System;
 
public struct S
{
    public readonly void M()
    {
        E += () => {};
        E -= () => {};
    }
 
    public readonly event Action E { add {} remove {} }
}
";
 
            var verifier = CreateCompilation(csharp);
            verifier.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyMethod_Field_EventAssignment()
        {
            var csharp = @"
#pragma warning disable 0067
using System;
 
public struct S2
{
    public event Action E;
}
 
public struct S1
{
    public S2 s2;
 
    public readonly void M()
    {
        s2.E += () => {};
        s2.E -= () => {};
    }
}
";
 
            // TODO: should warn in warning wave https://github.com/dotnet/roslyn/issues/33968
            var verifier = CreateCompilation(csharp);
            verifier.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyStruct_Field_EventAssignment()
        {
            var csharp = @"
#pragma warning disable 0067
using System;
 
public struct S2
{
    public event Action E;
}
 
public readonly struct S1
{
    public readonly S2 s2;
 
    public void M()
    {
        s2.E += () => {};
        s2.E -= () => {};
    }
}
";
 
            // TODO: should warn in warning wave https://github.com/dotnet/roslyn/issues/33968
            var verifier = CreateCompilation(csharp);
            verifier.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyMethod_EventAssignment_Error()
        {
            var csharp = @"
using System;
public struct S
{
    public event Action<EventArgs> E;
 
    public readonly void M()
    {
        E += handler;
        E -= handler;
        E = handler;
        E(new EventArgs());
 
        void handler(EventArgs args) { }
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (9,9): warning CS8656: Call to non-readonly member 'S.E.add' from a 'readonly' member results in an implicit copy of 'this'.
                //         E += handler;
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.add", "this").WithLocation(9, 9),
                // (10,9): warning CS8656: Call to non-readonly member 'S.E.remove' from a 'readonly' member results in an implicit copy of 'this'.
                //         E -= handler;
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "E").WithArguments("S.E.remove", "this").WithLocation(10, 9),
                // (11,9): error CS1604: Cannot assign to 'E' because it is read-only
                //         E = handler;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "E").WithArguments("E").WithLocation(11, 9));
        }
 
        [Fact]
        public void ReadOnlyEventAccessors()
        {
            var csharp = @"
using System;
public struct S
{
    public int i;
    public readonly event Action<EventArgs> E
    {
        add { i++; }
        remove { i--; }
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (8,15): error CS1604: Cannot assign to 'i' because it is read-only
                //         add { i++; }
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(8, 15),
                // (9,18): error CS1604: Cannot assign to 'i' because it is read-only
                //         remove { i--; }
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "i").WithArguments("i").WithLocation(9, 18));
        }
 
        [Fact]
        public void ReadOnlyStruct_CallNormalMethodOnField()
        {
            var csharp = @"
public readonly struct S1
{
    public readonly S2 s2;
    public void M1()
    {
        s2.M2();
    }
}
 
public struct S2
{
    public int i;
    public void M2()
    {
        i = 42;
    }
 
    static void Main()
    {
        var s1 = new S1();
        s1.M1();
        System.Console.Write(s1.s2.i);
    }
}
";
            // should warn about calling s2.M2 in warning wave (see https://github.com/dotnet/roslyn/issues/33968)
            CompileAndVerify(csharp, expectedOutput: "0");
        }
 
        [Fact]
        public void ReadOnlyMethod_CallNormalMethodOnField()
        {
            var csharp = @"
public struct S1
{
    public S2 s2;
    public readonly void M1()
    {
        s2.M2();
        System.Console.Write(s2.i);
 
        var copy = s2;
        copy.M2();
        System.Console.Write(copy.i);
    }
}
 
public struct S2
{
    public int i;
    public void M2()
    {
        i = 23;
    }
 
    static void Main()
    {
        var s1 = new S1() { s2 = new S2 { i = 1 } };
        s1.M1();
    }
}
";
            var verifier = CompileAndVerify(csharp, expectedOutput: "123");
            // should warn about calling s2.M2 in warning wave (see https://github.com/dotnet/roslyn/issues/33968)
            verifier.VerifyDiagnostics();
        }
 
        private static readonly string ilreadonlyStructWithWriteableFieldIL = @"
.class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute
       extends [mscorlib]System.Attribute
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Attribute::.ctor()
    IL_0006:  ret
  } // end of method EmbeddedAttribute::.ctor
 
} // end of class Microsoft.CodeAnalysis.EmbeddedAttribute
 
.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute
       extends [mscorlib]System.Attribute
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
  .custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Attribute::.ctor()
    IL_0006:  ret
  } // end of method IsReadOnlyAttribute::.ctor
 
} // end of class System.Runtime.CompilerServices.IsReadOnlyAttribute
 
 
 
.class public sequential ansi sealed beforefieldinit S1
       extends [mscorlib]System.ValueType
{
  .custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 ) 
 
   // WRITEABLE FIELD!!!
  .field public int32 'field'
 
} // end of class S1
 
";
 
        [Fact()]
        public void UseWriteableInstanceFieldsInRoStructs()
        {
            var csharp = @"
public class Program 
{ 
    public static void Main() 
    { 
        S1 s = new S1();
        s.field = 123;
        System.Console.WriteLine(s.field);
    }
}
";
 
            var comp = CreateCompilationWithILAndMscorlib40(csharp, ilreadonlyStructWithWriteableFieldIL, options: TestOptions.ReleaseExe);
 
            comp.VerifyDiagnostics();
 
            CompileAndVerify(comp, expectedOutput: "123");
        }
 
        [Fact()]
        public void UseWriteableInstanceFieldsInRoStructsErr()
        {
            var csharp = @"
public class Program 
{ 
    static readonly S1 s = new S1();
 
    public static void Main() 
    { 
        s.field = 123;
        System.Console.WriteLine(s.field);
    }
}
";
 
            var comp = CreateCompilationWithILAndMscorlib40(csharp, ilreadonlyStructWithWriteableFieldIL, options: TestOptions.ReleaseExe);
 
            comp.VerifyDiagnostics(
                // (8,9): error CS1650: Fields of static readonly field 'Program.s' cannot be assigned to (except in a static constructor or a variable initializer)
                //         s.field = 123;
                Diagnostic(ErrorCode.ERR_AssgReadonlyStatic2, "s.field").WithArguments("Program.s").WithLocation(8, 9)
                );
        }
 
        [Fact]
        public void ReadOnlyStructMethod()
        {
            var csharp = @"
public struct S
{
    public int i;
    public readonly int M()
    {
        return i;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics();
 
            var method = comp.GetMember<NamedTypeSymbol>("S").GetMember<MethodSymbol>("M");
            Assert.True(method.IsDeclaredReadOnly);
            Assert.True(method.IsEffectivelyReadOnly);
        }
 
        [Fact]
        public void ReadOnlyMembers_SemanticModel()
        {
            var csharp = @"
using System;
 
public struct S1
{
    public void M1() {}
    public readonly void M2() {}
 
    public int P1 { get; set; }
    public readonly int P2 => 42;
    public int P3 { readonly get => 123; }
    public int P4 { readonly set {} }
    public static int P5 { get; set; }
    public readonly event Action<EventArgs> E { add {} remove {} }
}
 
public readonly struct S2
{
    public void M1() {}
    public static void M2() {}
 
    public int P1 { get; }
    public int P2 => 42;
    public int P3 { set {} }
    public static int P4 { get; set; }
    public event Action<EventArgs> E { add {} remove {} }
}
";
            Compilation comp = CreateCompilation(csharp);
            var s1 = (INamedTypeSymbol)comp.GetSymbolsWithName("S1").Single();
 
            Assert.False(getMethod(s1, "M1").IsReadOnly);
 
            Assert.True(getMethod(s1, "M2").IsReadOnly);
 
            Assert.True(getProperty(s1, "P1").GetMethod.IsReadOnly);
            Assert.False(getProperty(s1, "P1").SetMethod.IsReadOnly);
 
            Assert.True(getProperty(s1, "P2").GetMethod.IsReadOnly);
 
            Assert.True(getProperty(s1, "P3").GetMethod.IsReadOnly);
 
            Assert.True(getProperty(s1, "P4").SetMethod.IsReadOnly);
 
            Assert.False(getProperty(s1, "P5").GetMethod.IsReadOnly);
            Assert.False(getProperty(s1, "P5").SetMethod.IsReadOnly);
 
            Assert.True(getEvent(s1, "E").AddMethod.IsReadOnly);
            Assert.True(getEvent(s1, "E").RemoveMethod.IsReadOnly);
 
            var s2 = comp.GetMember<INamedTypeSymbol>("S2");
            Assert.True(getMethod(s2, "M1").IsReadOnly);
            Assert.False(getMethod(s2, "M2").IsReadOnly);
 
            Assert.True(getProperty(s2, "P1").GetMethod.IsReadOnly);
 
            Assert.True(getProperty(s2, "P2").GetMethod.IsReadOnly);
 
            Assert.True(getProperty(s2, "P3").SetMethod.IsReadOnly);
 
            Assert.False(getProperty(s2, "P4").GetMethod.IsReadOnly);
            Assert.False(getProperty(s2, "P4").SetMethod.IsReadOnly);
 
            Assert.True(getEvent(s2, "E").AddMethod.IsReadOnly);
            Assert.True(getEvent(s2, "E").RemoveMethod.IsReadOnly);
 
            static IMethodSymbol getMethod(INamedTypeSymbol symbol, string name) => (IMethodSymbol)symbol.GetMembers(name).Single();
            static IPropertySymbol getProperty(INamedTypeSymbol symbol, string name) => (IPropertySymbol)symbol.GetMembers(name).Single();
            static IEventSymbol getEvent(INamedTypeSymbol symbol, string name) => (IEventSymbol)symbol.GetMembers(name).Single();
        }
 
        [Theory, CombinatorialData]
        public void ReadOnlyMembers_ExtensionMethods_SemanticModel([CombinatorialValues("in", "ref readonly")] string modifier)
        {
            var csharp = @"
public struct S1 {}
public readonly struct S2 {}
 
public static class C
{
    static void M1(this S1 s1) {}
    static void M2(this ref S1 s1) {}
    static void M3(this " + modifier + @" S1 s1) {}
    static void M4(this S2 s2) {}
    static void M5(this ref S2 s2) {}
    static void M6(this " + modifier + @" S2 s2) {}
 
    static void Test()
    {
        var s1 = new S1();
        s1.M1();
        s1.M2();
        s1.M3();
 
        var s2 = new S2();
        s2.M4();
        s2.M5();
        s2.M6();
    }
}
";
            Compilation comp = CreateCompilation(csharp);
 
            var c = comp.GetMember<IMethodSymbol>("C.Test");
            var testMethodSyntax = (MethodDeclarationSyntax)c.DeclaringSyntaxReferences.Single().GetSyntax();
 
            var semanticModel = comp.GetSemanticModel(testMethodSyntax.SyntaxTree);
            var statements = testMethodSyntax.Body.Statements;
 
            testStatement(statements[1], false);
            testStatement(statements[2], false);
            testStatement(statements[3], true);
 
            testStatement(statements[5], false);
            testStatement(statements[6], false);
            testStatement(statements[7], true);
 
            void testStatement(StatementSyntax statementSyntax, bool isEffectivelyReadOnly)
            {
                var expressionStatement = (ExpressionStatementSyntax)statementSyntax;
                var invocationExpression = (InvocationExpressionSyntax)expressionStatement.Expression;
 
                var symbol = (IMethodSymbol)semanticModel.GetSymbolInfo(invocationExpression.Expression).Symbol;
                var reducedFrom = symbol.ReducedFrom;
 
                Assert.Equal(isEffectivelyReadOnly, symbol.GetSymbol().IsEffectivelyReadOnly);
                Assert.Equal(isEffectivelyReadOnly, symbol.IsReadOnly);
 
                Assert.False(symbol.GetSymbol().IsDeclaredReadOnly);
                Assert.False(reducedFrom.GetSymbol().IsDeclaredReadOnly);
                Assert.False(reducedFrom.GetSymbol().IsEffectivelyReadOnly);
                Assert.False(((IMethodSymbol)reducedFrom).IsReadOnly);
            }
        }
 
        [Fact]
        public void ReadOnlyMembers_RefReturningProperty()
        {
            var csharp = @"
public struct S1
{
    private static int i = 0;
 
    public ref int P1 => ref i;
    public readonly ref int P2 => ref i;
    public ref readonly int P3 => ref i;
    public readonly ref readonly int P4 => ref i;
}
";
            var comp = CreateCompilation(csharp);
 
            var s1 = comp.GetMember<NamedTypeSymbol>("S1");
 
            check(s1.GetProperty("P1"), true, false, false);
            check(s1.GetProperty("P2"), true, false, true);
            check(s1.GetProperty("P3"), false, true, false);
            check(s1.GetProperty("P4"), false, true, true);
 
            static void check(PropertySymbol property, bool returnsByRef, bool returnsByRefReadonly, bool isReadOnly)
            {
                Assert.Equal(returnsByRef, property.ReturnsByRef);
                Assert.Equal(returnsByRefReadonly, property.ReturnsByRefReadonly);
 
                Assert.True(property.IsReadOnly);
                Assert.Equal(isReadOnly, property.GetMethod.IsDeclaredReadOnly);
                Assert.Equal(isReadOnly, property.GetMethod.IsEffectivelyReadOnly);
                Assert.Equal(isReadOnly, property.GetMethod.GetPublicSymbol().IsReadOnly);
            }
        }
 
        [Fact]
        public void ReadOnlyClass()
        {
            var csharp = @"
using System;
 
public readonly class C
{
    public readonly int M() => 42;
    public readonly int P { get; set; }
    public readonly int this[int i] => i;
    public readonly event Action<EventArgs> E { add {} remove {} }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,23): error CS0106: The modifier 'readonly' is not valid for this item
                // public readonly class C
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("readonly").WithLocation(4, 23),
                // (6,25): error CS0106: The modifier 'readonly' is not valid for this item
                //     public readonly int M() => 42;
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("readonly").WithLocation(6, 25),
                // (7,25): error CS0106: The modifier 'readonly' is not valid for this item
                //     public readonly int P { get; set; }
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("readonly").WithLocation(7, 25),
                // (8,25): error CS0106: The modifier 'readonly' is not valid for this item
                //     public readonly int this[int i] => i;
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("readonly").WithLocation(8, 25),
                // (9,45): error CS0106: The modifier 'readonly' is not valid for this item
                //     public readonly event Action<EventArgs> E { add {} remove {} }
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(9, 45));
        }
 
        [Fact]
        public void ReadOnlyClass_NormalMethod()
        {
            var csharp = @"
public readonly class C
{
    int i;
    void M()
    {
        i++;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (2,23): error CS0106: The modifier 'readonly' is not valid for this item
                // public readonly class C
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("readonly").WithLocation(2, 23));
        }
 
        [Fact]
        public void ReadOnlyInterface()
        {
            var csharp = @"
using System;
 
public readonly interface I
{
    readonly int M();
    readonly int P { get; set; }
    readonly int this[int i] { get; }
    readonly event Action<EventArgs> E;
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,27): error CS0106: The modifier 'readonly' is not valid for this item
                // public readonly interface I
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "I").WithArguments("readonly").WithLocation(4, 27),
                // (6,18): error CS0106: The modifier 'readonly' is not valid for this item
                //     readonly int M();
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("readonly").WithLocation(6, 18),
                // (7,18): error CS0106: The modifier 'readonly' is not valid for this item
                //     readonly int P { get; set; }
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("readonly").WithLocation(7, 18),
                // (8,18): error CS0106: The modifier 'readonly' is not valid for this item
                //     readonly int this[int i] { get; }
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("readonly").WithLocation(8, 18),
                // (9,38): error CS0106: The modifier 'readonly' is not valid for this item
                //     readonly event Action<EventArgs> E;
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(9, 38));
        }
 
        [Fact]
        public void ReadOnlyEnum()
        {
            var csharp = @"
public readonly enum E
{
    readonly A, readonly B
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (2,22): error CS0106: The modifier 'readonly' is not valid for this item
                // public readonly enum E
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(2, 22),
                // (3,2): error CS1041: Identifier expected; 'readonly' is a keyword
                // {
                Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "").WithArguments("", "readonly").WithLocation(3, 2),
                // (4,17): error CS1041: Identifier expected; 'readonly' is a keyword
                //     readonly A, readonly B;
                Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "readonly").WithArguments("", "readonly").WithLocation(4, 17));
        }
 
        [Fact]
        public void ReadOnlyStructStaticMethod()
        {
            var csharp = @"
public struct S
{
    public static int i;
    public static readonly int M()
    {
        return i;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (5,32): error CS8656: Static member 'S.M()' cannot be marked 'readonly'.
                //     public static readonly int M()
                Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "M").WithArguments("S.M()").WithLocation(5, 32));
 
            var method = comp.GetMember<NamedTypeSymbol>("S").GetMethod("M");
            Assert.True(method.IsDeclaredReadOnly);
            Assert.False(method.IsEffectivelyReadOnly);
        }
 
        [Fact]
        public void ReadOnlyStructProperty()
        {
            var csharp = @"
public struct S
{
    public int i;
    public readonly int P
    {
        get
        {
            return i;
        }
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyStructStaticProperty()
        {
            var csharp = @"
public struct S
{
    public static int i;
    public static int P
    {
        readonly get
        {
            return i;
        }
        set
        {
            i = value;
        }
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (7,18): error CS8656: Static member 'S.P.get' cannot be marked 'readonly'.
                //         readonly get
                Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "get").WithArguments("S.P.get").WithLocation(7, 18));
        }
 
        [Fact]
        public void ReadOnlyStructStaticExpressionProperty()
        {
            var csharp = @"
public struct S
{
    public static int i;
    public static readonly int P => i;
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (5,32): error CS8656: Static member 'S.P' cannot be marked 'readonly'.
                //     public static readonly int P => i;
                Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "P").WithArguments("S.P").WithLocation(5, 32));
        }
 
        [Fact]
        public void ReadOnlyStructExpressionProperty()
        {
            var csharp = @"
public struct S
{
    public int i;
    public readonly int P => i;
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyStructBlockProperty()
        {
            var csharp = @"
public struct S
{
    public int i;
    public readonly int P { get { return i; } }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyAutoProperty()
        {
            var csharp = @"
public struct S
{
    public int P1 { readonly get; }
    public readonly int P2 { get; }
    public int P3 { readonly get; set; }
    public int P4 { readonly get; readonly set; }
    public readonly int P5 { get; set; }
    public readonly int P6 { readonly get; }
    public int P7 { get; readonly set; }
    public readonly int P8 { get; readonly set; }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,16): error CS8663: 'S.P1': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor
                //     public int P1 { readonly get; }
                Diagnostic(ErrorCode.ERR_ReadOnlyModMissingAccessor, "P1").WithArguments("S.P1").WithLocation(4, 16),
                // (7,16): error CS8660: Cannot specify 'readonly' modifiers on both accessors of property or indexer 'S.P4'. Instead, put a 'readonly' modifier on the property itself.
                //     public int P4 { readonly get; readonly set; }
                Diagnostic(ErrorCode.ERR_DuplicatePropertyReadOnlyMods, "P4").WithArguments("S.P4").WithLocation(7, 16),
                // (7,44): error CS8657: Auto-implemented 'set' accessor 'S.P4.set' cannot be marked 'readonly'.
                //     public int P4 { readonly get; readonly set; }
                Diagnostic(ErrorCode.ERR_AutoSetterCantBeReadOnly, "set").WithArguments("S.P4.set").WithLocation(7, 44),
                // (8,25): error CS8658: Auto-implemented property 'S.P5' cannot be marked 'readonly' because it has a 'set' accessor.
                //     public readonly int P5 { get; set; }
                Diagnostic(ErrorCode.ERR_AutoPropertyWithSetterCantBeReadOnly, "P5").WithArguments("S.P5").WithLocation(8, 25),
                // (9,25): error CS8663: 'S.P6': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor
                //     public readonly int P6 { readonly get; }
                Diagnostic(ErrorCode.ERR_ReadOnlyModMissingAccessor, "P6").WithArguments("S.P6").WithLocation(9, 25),
                // (9,39): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S.P6' and its accessor. Remove one of them.
                //     public readonly int P6 { readonly get; }
                Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "get").WithArguments("S.P6").WithLocation(9, 39),
                // (10,35): error CS8657: Auto-implemented 'set' accessor 'S.P7.set' cannot be marked 'readonly'.
                //     public int P7 { get; readonly set; }
                Diagnostic(ErrorCode.ERR_AutoSetterCantBeReadOnly, "set").WithArguments("S.P7.set").WithLocation(10, 35),
                // (11,25): error CS8658: Auto-implemented property 'S.P8' cannot be marked 'readonly' because it has a 'set' accessor.
                //     public readonly int P8 { get; readonly set; }
                Diagnostic(ErrorCode.ERR_AutoPropertyWithSetterCantBeReadOnly, "P8").WithArguments("S.P8").WithLocation(11, 25),
                // (11,44): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S.P8' and its accessor. Remove one of them.
                //     public readonly int P8 { get; readonly set; }
                Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "set").WithArguments("S.P8").WithLocation(11, 44));
        }
 
        [Fact]
        public void NetModule_ImplicitReadOnlyAutoProperty()
        {
            var csharp = @"
public struct S
{
    public int P1 { get; private set; }
}
";
            var moduleMetadata = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference();
            var moduleComp = CreateCompilation("", new[] { moduleMetadata });
            var moduleGetter = moduleComp.GetMember<PropertySymbol>("S.P1").GetMethod;
            Assert.False(moduleGetter.IsDeclaredReadOnly);
 
            var dllMetadata = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference();
            var dllComp = CreateCompilation("", new[] { dllMetadata });
            var dllGetter = dllComp.GetMember<PropertySymbol>("S.P1").GetMethod;
            Assert.True(dllGetter.IsDeclaredReadOnly);
        }
 
        [Fact]
        public void NetModule_ImplicitReadOnlyAutoProperty_MalformedAttribute()
        {
            var csharp = @"
namespace System.Runtime.CompilerServices
{
    public class IsReadOnlyAttribute
    {
        public IsReadOnlyAttribute(int x) {}
    }
}
 
public struct S
{
    public int P1 { get; private set; }
}
";
            var moduleMetadata = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference();
            var moduleComp = CreateCompilation("", new[] { moduleMetadata });
            var moduleGetter = moduleComp.GetMember<PropertySymbol>("S.P1").GetMethod;
            Assert.False(moduleGetter.IsDeclaredReadOnly);
 
            var dllMetadata = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461).EmitToImageReference();
            var dllComp = CreateCompilation("", new[] { dllMetadata });
            var dllGetter = dllComp.GetMember<PropertySymbol>("S.P1").GetMethod;
            Assert.False(dllGetter.IsDeclaredReadOnly);
        }
 
        [Fact]
        public void NetModule_ExplicitReadOnlyAutoProperty_MalformedAttribute()
        {
            var csharp = @"
namespace System.Runtime.CompilerServices
{
    public class IsReadOnlyAttribute
    {
        public IsReadOnlyAttribute(int x) {}
    }
}
 
public struct S
{
    public int P1 { readonly get; private set; }
}
";
            var moduleComp = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461);
            moduleComp.VerifyDiagnostics(
                // (12,30): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor'
                //     public int P1 { readonly get; private set; }
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "get").WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute", ".ctor").WithLocation(12, 30));
 
            var dllComp = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461);
            dllComp.VerifyDiagnostics(
                // (12,30): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor'
                //     public int P1 { readonly get; private set; }
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "get").WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute", ".ctor").WithLocation(12, 30));
        }
 
        [Fact]
        public void NetModule_ExplicitReadOnlyAutoProperty()
        {
            var csharp = @"
public struct S
{
    public int P1 { readonly get; private set; }
}
";
            var moduleComp = CreateCompilation(csharp, options: TestOptions.DebugModule, targetFramework: TargetFramework.Mscorlib461);
            moduleComp.VerifyDiagnostics(
                // (4,30): error CS0518: Predefined type 'System.Runtime.CompilerServices.IsReadOnlyAttribute' is not defined or imported
                //     public int P1 { readonly get; private set; }
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "get").WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute").WithLocation(4, 30));
 
            var dllComp = CreateCompilation(csharp, options: TestOptions.DebugDll, targetFramework: TargetFramework.Mscorlib461);
            dllComp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyProperty_RedundantReadOnlyAccessor()
        {
            var csharp = @"
public struct S
{
    public readonly int P { readonly get => 42; set {} }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,38): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S.P' and its accessor. Remove one of them.
                //     public readonly int P { readonly get => 42; }
                Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "get").WithArguments("S.P").WithLocation(4, 38));
        }
 
        [Fact]
        public void ReadOnlyStaticAutoProperty()
        {
            var csharp = @"
public struct S
{
    public static readonly int P1 { get; set; }
    public static int P2 { readonly get; set; }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,32): error CS8656: Static member 'S.P1' cannot be marked 'readonly'.
                //     public static readonly int P1 { get; set; }
                Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "P1").WithArguments("S.P1").WithLocation(4, 32),
                // (5,37): error CS8656: Static member 'S.P2.get' cannot be marked 'readonly'.
                //     public static int P2 { readonly get; }
                Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "get").WithArguments("S.P2.get").WithLocation(5, 37));
        }
 
        [Fact]
        public void RefReturningReadOnlyMethod()
        {
            var csharp = @"
public struct S
{
    private static int f1;
    public readonly ref int M1_1() => ref f1; // ok
    public readonly ref readonly int M1_2() => ref f1; // ok
 
    private static readonly int f2;
    public readonly ref int M2_1() => ref f2; // error
    public readonly ref readonly int M2_2() => ref f2; // ok
 
    private static readonly int f3;
    public ref readonly int M3()
    {
        f1++;
        return ref f3;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (9,43): error CS8161: A static readonly field cannot be returned by writable reference
                //     public readonly ref int M2_1() => ref f2; // error
                Diagnostic(ErrorCode.ERR_RefReturnReadonlyStatic, "f2").WithLocation(9, 43));
        }
 
        [Fact]
        public void ReadOnlyConstructor()
        {
            var csharp = @"
public struct S
{
    static readonly S() { }
    public readonly S(int i) { }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,21): error CS0106: The modifier 'readonly' is not valid for this item
                //     static readonly S() { }
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(4, 21),
                // (5,21): error CS0106: The modifier 'readonly' is not valid for this item
                //     public readonly S(int i) { }
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(5, 21));
        }
 
        [Fact]
        public void ReadOnlyStruct_Constructor()
        {
            var csharp = @"
public readonly struct S
{
    public readonly int i;
    public S(int i)
    {
        this.i = i; // ok
        M(ref this); // ok
    }
    public static void M(ref S s)
    {
        s.i = 42; // error
        s = default; // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (12,9): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)
                //         s.i = 42; // error
                Diagnostic(ErrorCode.ERR_AssgReadonly, "s.i").WithLocation(12, 9));
        }
 
        [Fact]
        public void ReadOnlyMethod_GetEnumerator()
        {
            var csharp = @"
using System.Collections;
 
public struct S1
{
    public IEnumerator GetEnumerator() => throw null;
    void M1()
    {
        foreach (var x in this) {} // ok
    }
    readonly void M2()
    {
        foreach (var x in this) {} // warning-- implicit copy
    }
}
 
public struct S2
{
    public readonly IEnumerator GetEnumerator() => throw null;
    void M1()
    {
        foreach (var x in this) {} // ok
    }
    readonly void M2()
    {
        foreach (var x in this) {} // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (13,27): warning CS8655: Call to non-readonly member 'S1.GetEnumerator()' from a 'readonly' member results in an implicit copy of 'this'.
                //         foreach (var x in this) {} // warning-- implicit copy
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetEnumerator()", "this").WithLocation(13, 27));
        }
 
        [Fact]
        public void ReadOnlyMethod_GetEnumerator_MethodMissing()
        {
            var csharp = @"
public struct S1
{
    readonly void M2()
    {
        foreach (var x in this) {}
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (6,27): error CS1579: foreach statement cannot operate on variables of type 'S1' because 'S1' does not contain a public instance or extension definition for 'GetEnumerator'
                //         foreach (var x in this) {}
                Diagnostic(ErrorCode.ERR_ForEachMissingMember, "this").WithArguments("S1", "GetEnumerator").WithLocation(6, 27));
        }
 
        [Fact]
        public void ReadOnlyMethod_AsyncStreams()
        {
            var csharp = @"
using System.Threading.Tasks;
using System.Collections.Generic;
 
public struct S1
{
    public IAsyncEnumerator<int> GetAsyncEnumerator() => throw null;
 
    public async Task M1()
    {
        await foreach (var x in this) {}
    }
 
    public readonly async Task M2()
    {
        await foreach (var x in this) {} // warn
    }
}
 
public struct S2
{
    public readonly IAsyncEnumerator<int> GetAsyncEnumerator() => throw null;
 
    public async Task M1()
    {
        await foreach (var x in this) {}
    }
 
    public readonly async Task M2()
    {
        await foreach (var x in this) {} // ok
    }
}
";
            var comp = CreateCompilationWithTasksExtensions(new[] { csharp, AsyncStreamsTypes });
            comp.VerifyDiagnostics(
                // (16,33): warning CS8655: Call to non-readonly member 'S1.GetAsyncEnumerator()' from a 'readonly' member results in an implicit copy of 'this'.
                //         await foreach (var x in this) {} // warn
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.GetAsyncEnumerator()", "this").WithLocation(16, 33));
        }
 
        [Fact]
        public void ReadOnlyMethod_Using()
        {
            // 'using' results in a boxing conversion when the struct implements 'IDisposable'.
            // Boxing conversions are out of scope of the implicit copy warning.
            // 'await using' can't be used with ref structs, so implicitly copy warnings can't be produced in that scenario.
            var csharp = @"
public ref struct S1
{
    public void Dispose() {}
 
    void M1()
    {
        using (this) { } // ok
    }
 
    readonly void M2()
    {
        using (this) { } // should warn
    }
}
 
public ref struct S2
{
    public readonly void Dispose() {}
 
    void M1()
    {
        using (this) { } // ok
    }
 
    readonly void M2()
    {
        using (this) { } // ok
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (13,16): warning CS8655: Call to non-readonly member 'S1.Dispose()' from a 'readonly' member results in an implicit copy of 'this'.
                //         using (this) { } // should warn
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.Dispose()", "this").WithLocation(13, 16));
        }
 
        [Fact]
        public void ReadOnlyMethod_Deconstruct()
        {
            var csharp = @"
public struct S1
{
    void M1()
    {
        var (x, y) = this; // ok
    }
 
    readonly void M2()
    {
        var (x, y) = this; // should warn
    }
 
    public void Deconstruct(out int x, out int y)
    {
        x = 42;
        y = 123;
    }
}
 
public struct S2
{
    void M1()
    {
        var (x, y) = this; // ok
    }
 
    readonly void M2()
    {
        var (x, y) = this; // ok
    }
 
    public readonly void Deconstruct(out int x, out int y)
    {
        x = 42;
        y = 123;
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (11,22): warning CS8655: Call to non-readonly member 'S1.Deconstruct(out int, out int)' from a 'readonly' member results in an implicit copy of 'this'.
                //         var (x, y) = this; // should warn
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S1.Deconstruct(out int, out int)", "this").WithLocation(11, 22));
        }
 
        [Fact]
        public void ReadOnlyMethod_Deconstruct_MethodMissing()
        {
            var csharp = @"
public struct S2
{
    readonly void M1()
    {
        var (x, y) = this; // error
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (6,14): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x'.
                //         var (x, y) = this; // error
                Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x").WithArguments("x").WithLocation(6, 14),
                // (6,17): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y'.
                //         var (x, y) = this; // error
                Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(6, 17),
                // (6,22): error CS1061: 'S2' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'S2' could be found (are you missing a using directive or an assembly reference?)
                //         var (x, y) = this; // error
                Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "this").WithArguments("S2", "Deconstruct").WithLocation(6, 22),
                // (6,22): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'S2', with 2 out parameters and a void return type.
                //         var (x, y) = this; // error
                Diagnostic(ErrorCode.ERR_MissingDeconstruct, "this").WithArguments("S2", "2").WithLocation(6, 22));
        }
 
        [Fact]
        public void ReadOnlyDestructor()
        {
            var csharp = @"
public struct S
{
    readonly ~S() { }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,15): error CS0106: The modifier 'readonly' is not valid for this item
                //     readonly ~S() { }
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(4, 15),
                // (4,15): error CS0575: Only class types can contain destructors
                //     readonly ~S() { }
                Diagnostic(ErrorCode.ERR_OnlyClassesCanContainDestructors, "S").WithLocation(4, 15));
        }
 
        [Fact]
        public void ReadOnlyOperator()
        {
            var csharp = @"
public struct S
{
    public static readonly S operator +(S lhs, S rhs) => lhs;
    public static readonly explicit operator int(S s) => 42;
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (4,39): error CS0106: The modifier 'readonly' is not valid for this item
                //     public static readonly S operator +(S lhs, S rhs) => lhs;
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("readonly").WithLocation(4, 39),
                // (5,46): error CS0106: The modifier 'readonly' is not valid for this item
                //     public static readonly explicit operator int(S s) => 42;
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("readonly").WithLocation(5, 46));
        }
 
        [Fact]
        public void ReadOnlyDelegate()
        {
            var csharp = @"
public readonly delegate int Del();
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (2,30): error CS0106: The modifier 'readonly' is not valid for this item
                // public readonly delegate int Del();
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "Del").WithArguments("readonly").WithLocation(2, 30));
        }
 
        [Fact]
        public void ReadOnlyLocalFunction()
        {
            var csharp = @"
public struct S
{
    void M1()
    {
        local();
        readonly void local() {}
    }
    readonly void M2()
    {
        local();
        readonly void local() {}
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (7,9): error CS0106: The modifier 'readonly' is not valid for this item
                //         readonly void local() {}
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(7, 9),
                // (12,9): error CS0106: The modifier 'readonly' is not valid for this item
                //         readonly void local() {}
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(12, 9));
        }
 
        [Fact]
        public void ReadOnlyLambda()
        {
            var csharp = @"
public struct S
{
    void M1()
    {
        M2(readonly () => 42);
    }
    void M2(System.Func<int> a)
    {
        _ = a();
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (6,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'S.M2(Func<int>)'
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M2").WithArguments("a", "S.M2(System.Func<int>)").WithLocation(6, 9),
                // (6,12): error CS1026: ) expected
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(6, 12),
                // (6,12): error CS1002: ; expected
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(6, 12),
                // (6,12): error CS0106: The modifier 'readonly' is not valid for this item
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(6, 12),
                // (6,22): error CS8124: Tuple must contain at least two elements.
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(6, 22),
                // (6,24): error CS1001: Identifier expected
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_IdentifierExpected, "=>").WithLocation(6, 24),
                // (6,24): error CS1003: Syntax error, ',' expected
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_SyntaxError, "=>").WithArguments(",").WithLocation(6, 24),
                // (6,27): error CS1002: ; expected
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_SemicolonExpected, "42").WithLocation(6, 27),
                // (6,29): error CS1002: ; expected
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(6, 29),
                // (6,29): error CS1513: } expected
                //         M2(readonly () => 42);
                Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(6, 29));
        }
 
        [Fact]
        public void ReadOnlyIndexer()
        {
            var csharp = @"
public struct S1
{
    // ok
    public readonly int this[int i] => i;
}
 
public struct S2
{
    // ok
    public int this[int i]
    {
        readonly get => i;
        set {}
    }
}
 
public struct S3
{
    // error
    public int this[int i]
    {
        readonly get { return i; }
        readonly set {}
    }
}
 
public struct S4
{
    // error
    public int this[int i]
    {
        readonly get { return i; }
    }
}
 
public struct S5
{
    // error
    public readonly int this[int i]
    {
        readonly get { return i; }
        set { }
    }
}
 
public struct S6
{
    // error
    public static readonly int this[int i] => i;
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (21,16): error CS8660: Cannot specify 'readonly' modifiers on both accessors of property or indexer 'S3.this[int]'. Instead, put a 'readonly' modifier on the property itself.
                //     public int this[int i]
                Diagnostic(ErrorCode.ERR_DuplicatePropertyReadOnlyMods, "this").WithArguments("S3.this[int]").WithLocation(21, 16),
                // (31,16): error CS8663: 'S4.this[int]': 'readonly' can only be used on accessors if the property or indexer has both a get and a set accessor
                //     public int this[int i]
                Diagnostic(ErrorCode.ERR_ReadOnlyModMissingAccessor, "this").WithArguments("S4.this[int]").WithLocation(31, 16),
                // (42,18): error CS8659: Cannot specify 'readonly' modifiers on both property or indexer 'S5.this[int]' and its accessor. Remove one of them.
                //         readonly get { return i; }
                Diagnostic(ErrorCode.ERR_InvalidPropertyReadOnlyMods, "get").WithArguments("S5.this[int]").WithLocation(42, 18),
                // (50,32): error CS0106: The modifier 'static' is not valid for this item
                //     public static readonly int this[int i] => i;
                Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(50, 32));
        }
 
        [Fact]
        public void ReadOnlyFieldLikeEvent()
        {
            var csharp = @"
using System;
 
public struct S1
{
    public readonly event Action<EventArgs> E;
    public void M() { E?.Invoke(new EventArgs()); }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (6,45): error CS8661: Field-like event 'S1.E' cannot be 'readonly'.
                //     public readonly event Action<EventArgs> E;
                Diagnostic(ErrorCode.ERR_FieldLikeEventCantBeReadOnly, "E").WithArguments("S1.E").WithLocation(6, 45));
        }
 
        [Fact]
        public void ReadOnlyEventExplicitAddRemove()
        {
            var csharp = @"
using System;
 
public struct S1
{
    public readonly event Action<EventArgs> E
    {
        add {}
        remove {}
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyStaticEvent()
        {
            var csharp = @"
using System;
 
public struct S1
{
    public static readonly event Action<EventArgs> E
    {
        add {}
        remove {}
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (6,52): error CS8656: Static member 'S1.E' cannot be marked 'readonly'.
                //     public static readonly event Action<EventArgs> E
                Diagnostic(ErrorCode.ERR_StaticMemberCantBeReadOnly, "E").WithArguments("S1.E").WithLocation(6, 52));
        }
 
        [Fact]
        public void ReadOnlyEventReadOnlyAccessors()
        {
            var csharp = @"
using System;
 
public struct S1
{
    public event Action<EventArgs> E
    {
        readonly add {}
        readonly remove {}
    }
}
";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (8,9): error CS1609: Modifiers cannot be placed on event accessor declarations
                //         readonly add {}
                Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "readonly").WithLocation(8, 9),
                // (9,9): error CS1609: Modifiers cannot be placed on event accessor declarations
                //         readonly remove {}
                Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "readonly").WithLocation(9, 9));
        }
 
        [Fact]
        public void ReadOnlyMembers_LangVersion()
        {
            var csharp = @"
using System;
 
public struct S
{
    public readonly void M() {}
 
    public readonly int P1 => 42;
    public int P2 { readonly get => 123; set {} }
    public int P3 { get => 123; readonly set {} }
 
    public readonly int this[int i] => i;
    public int this[int i, int j] { readonly get => i + j; set {} }
 
    public readonly event Action<EventArgs> E { add {} remove {} }
}
";
            var comp = CreateCompilation(csharp, parseOptions: TestOptions.Regular7_3);
            comp.VerifyDiagnostics(
                // (6,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
                //     public readonly void M() {}
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(6, 12),
                // (8,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
                //     public readonly int P1 => 42;
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(8, 12),
                // (9,21): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
                //     public int P2 { readonly get => 123; set {} }
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(9, 21),
                // (10,33): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
                //     public int P3 { get => 123; readonly set {} }
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(10, 33),
                // (12,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
                //     public readonly int this[int i] => i;
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(12, 12),
                // (13,37): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
                //     public int this[int i, int j] { readonly get => i + j; set {} }
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(13, 37),
                // (15,12): error CS8652: The feature 'readonly members' is not available in C# 7.3. Please use language version 8.0 or greater.
                //     public readonly event Action<EventArgs> E { add {} remove {} }
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "readonly").WithArguments("readonly members", "8.0").WithLocation(15, 12));
 
            comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ReadOnlyMethod_CompoundPropertyAssignment()
        {
            var csharp = @"
struct S
{
    int P1 { get => 123; set {} }
    int P2 { readonly get => 123; set {} }
    int P3 { get => 123; readonly set {} }
    readonly int P4 { get => 123; set {} }
 
    void M1()
    {
        // ok
        P1 += 1;
        P2 += 1;
        P3 += 1;
        P4 += 1;
    }
 
    readonly void M2()
    {
        P1 += 1; // error
        P2 += 1; // error
        P3 += 1; // warning
        P4 += 1; // ok
    }
}";
            var comp = CreateCompilation(csharp);
            comp.VerifyDiagnostics(
                // (20,9): error CS1604: Cannot assign to 'P1' because it is read-only
                //         P1 += 1; // error
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "P1").WithArguments("P1").WithLocation(20, 9),
                // (21,9): error CS1604: Cannot assign to 'P2' because it is read-only
                //         P2 += 1; // error
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "P2").WithArguments("P2").WithLocation(21, 9),
                // (22,9): warning CS8655: Call to non-readonly member 'S.P3.get' from a 'readonly' member results in an implicit copy of 'this'.
                //         P3 += 1; // warning
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "P3").WithArguments("S.P3.get", "this").WithLocation(22, 9));
        }
    }
}