Allow init accessors in readonly contexts

This allows `init` properties on readonly structs, readonly properties in structs, and allows `init` accessors to be declared readonly themselves. Fixes https://github.com/dotnet/roslyn/issues/47612.
上级 fd307674
......@@ -373,7 +373,7 @@ internal virtual bool IsExplicitInterfaceImplementation
/// Indicates whether the method is effectively readonly,
/// by either the method or the containing type being marked readonly.
/// </summary>
internal virtual bool IsEffectivelyReadOnly => (IsDeclaredReadOnly || ContainingType?.IsReadOnly == true) && IsValidReadOnlyTarget;
internal virtual bool IsEffectivelyReadOnly => (IsDeclaredReadOnly || ContainingType?.IsReadOnly == true) && IsValidReadOnlyTarget && !IsInitOnly;
protected bool IsValidReadOnlyTarget => !IsStatic && ContainingType.IsStructType() && MethodKind != MethodKind.Constructor;
......
......@@ -673,7 +673,7 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty
// Static member '{0}' cannot be marked 'readonly'.
diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this);
}
else if (LocalDeclaredReadOnly && _isAutoPropertyAccessor && MethodKind == MethodKind.PropertySet)
else if (LocalDeclaredReadOnly && _isAutoPropertyAccessor && MethodKind == MethodKind.PropertySet && !_usesInit)
{
// Auto-implemented accessor '{0}' cannot be marked 'readonly'.
diagnostics.Add(ErrorCode.ERR_AutoSetterCantBeReadOnly, location, this);
......
......@@ -161,7 +161,7 @@ private enum Flags : byte
bool isGetterOnly = hasGetAccessor && !hasSetAccessor;
if (isAutoPropertyWithGetSyntax && !IsStatic && !isGetterOnly)
if (isAutoPropertyWithGetSyntax && !IsStatic && !isGetterOnly && !isInitOnly)
{
if (ContainingType.IsReadOnly)
{
......
......@@ -3774,5 +3774,218 @@ public static void Main()
var setter = (RetargetingMethodSymbol)property.SetMethod;
Assert.True(setter.IsInitOnly);
}
[Fact]
[WorkItem(47612, "https://github.com/dotnet/roslyn/issues/47612")]
public void InitOnlyOnReadonlyStruct_AutoProp()
{
var comp = CompileAndVerify(new[] { IsExternalInitTypeDefinition, @"
var s = new S { I = 1 };
System.Console.Write(s.I);
public readonly struct S
{
public int I { get; init; }
}
" }, expectedOutput: "1"); ;
comp.VerifyIL("<top-level-statements-entry-point>", @"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (S V_0, //s
S V_1)
IL_0000: ldloca.s V_1
IL_0002: initobj ""S""
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.1
IL_000b: call ""void S.I.init""
IL_0010: ldloc.1
IL_0011: stloc.0
IL_0012: ldloca.s V_0
IL_0014: call ""int S.I.get""
IL_0019: call ""void System.Console.Write(int)""
IL_001e: ret
}
");
}
[Fact]
[WorkItem(47612, "https://github.com/dotnet/roslyn/issues/47612")]
public void InitOnlyOnReadonlyStruct_ManualProp()
{
var comp = CompileAndVerify(new[] { IsExternalInitTypeDefinition, @"
var s = new S { I = 1 };
System.Console.Write(s.I);
public readonly struct S
{
private readonly int i;
public int I { get => i; init => i = value; }
}
" }, expectedOutput: "1"); ;
comp.VerifyIL("<top-level-statements-entry-point>", @"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (S V_0, //s
S V_1)
IL_0000: ldloca.s V_1
IL_0002: initobj ""S""
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.1
IL_000b: call ""void S.I.init""
IL_0010: ldloc.1
IL_0011: stloc.0
IL_0012: ldloca.s V_0
IL_0014: call ""int S.I.get""
IL_0019: call ""void System.Console.Write(int)""
IL_001e: ret
}
");
}
[Fact]
[WorkItem(47612, "https://github.com/dotnet/roslyn/issues/47612")]
public void InitOnlyOnReadonlyProperty_AutoProp()
{
var comp = CompileAndVerify(new[] { IsExternalInitTypeDefinition, @"
var s = new S { I = 1 };
System.Console.Write(s.I);
public struct S
{
public readonly int I { get; init; }
}
" }, expectedOutput: "1"); ;
comp.VerifyIL("<top-level-statements-entry-point>", @"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (S V_0, //s
S V_1)
IL_0000: ldloca.s V_1
IL_0002: initobj ""S""
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.1
IL_000b: call ""readonly void S.I.init""
IL_0010: ldloc.1
IL_0011: stloc.0
IL_0012: ldloca.s V_0
IL_0014: call ""readonly int S.I.get""
IL_0019: call ""void System.Console.Write(int)""
IL_001e: ret
}
");
}
[Fact]
[WorkItem(47612, "https://github.com/dotnet/roslyn/issues/47612")]
public void InitOnlyOnReadonlyProperty_ManualProp()
{
var comp = CompileAndVerify(new[] { IsExternalInitTypeDefinition, @"
var s = new S { I = 1 };
System.Console.Write(s.I);
public struct S
{
private readonly int i;
public readonly int I { get => i; init => i = value; }
}
" }, expectedOutput: "1"); ;
comp.VerifyIL("<top-level-statements-entry-point>", @"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (S V_0, //s
S V_1)
IL_0000: ldloca.s V_1
IL_0002: initobj ""S""
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.1
IL_000b: call ""readonly void S.I.init""
IL_0010: ldloc.1
IL_0011: stloc.0
IL_0012: ldloca.s V_0
IL_0014: call ""readonly int S.I.get""
IL_0019: call ""void System.Console.Write(int)""
IL_001e: ret
}
");
}
[Fact]
[WorkItem(47612, "https://github.com/dotnet/roslyn/issues/47612")]
public void InitOnlyOnReadonlyInit_AutoProp()
{
var comp = CompileAndVerify(new[] { IsExternalInitTypeDefinition, @"
var s = new S { I = 1 };
System.Console.Write(s.I);
public struct S
{
public int I { get; readonly init; }
}
" }, expectedOutput: "1"); ;
comp.VerifyIL("<top-level-statements-entry-point>", @"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (S V_0, //s
S V_1)
IL_0000: ldloca.s V_1
IL_0002: initobj ""S""
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.1
IL_000b: call ""readonly void S.I.init""
IL_0010: ldloc.1
IL_0011: stloc.0
IL_0012: ldloca.s V_0
IL_0014: call ""readonly int S.I.get""
IL_0019: call ""void System.Console.Write(int)""
IL_001e: ret
}
");
}
[Fact]
[WorkItem(47612, "https://github.com/dotnet/roslyn/issues/47612")]
public void InitOnlyOnReadonlyInit_ManualProp()
{
var comp = CompileAndVerify(new[] { IsExternalInitTypeDefinition, @"
var s = new S { I = 1 };
System.Console.Write(s.I);
public struct S
{
private readonly int i;
public int I { get => i; readonly init => i = value; }
}
" }, expectedOutput: "1"); ;
comp.VerifyIL("<top-level-statements-entry-point>", @"
{
// Code size 31 (0x1f)
.maxstack 2
.locals init (S V_0, //s
S V_1)
IL_0000: ldloca.s V_1
IL_0002: initobj ""S""
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.1
IL_000b: call ""readonly void S.I.init""
IL_0010: ldloc.1
IL_0011: stloc.0
IL_0012: ldloca.s V_0
IL_0014: call ""int S.I.get""
IL_0019: call ""void System.Console.Write(int)""
IL_001e: ret
}
");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册