From 0fd202e733e441fff206abaac9fe545e5ff1768b Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 12 Jan 2021 10:52:54 -0800 Subject: [PATCH 1/2] Reference conversion on this in init-only phase --- .../Portable/Binder/Binder.ValueChecks.cs | 11 ++++ .../Semantic/Semantics/InitOnlyMemberTests.cs | 54 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 2ca3f79498740..4ab816f030270 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1196,6 +1196,17 @@ bool isAllowedInitOnlySet(BoundExpression receiver) return true; } + while (receiver is BoundConversion boundConversion) + { + var conversion = boundConversion.Conversion; + if (!(conversion.IsIdentity || conversion.IsReference)) + { + return false; + } + + receiver = boundConversion.Operand; + } + // bad: other.InitOnlyProperty = ... if (!(receiver is BoundThisReference || receiver is BoundBaseReference)) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs index 528d9f9f88134..e2f129ccba9da 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs @@ -1660,6 +1660,60 @@ public string RegularProperty2 ); } + [Fact, WorkItem(50053, "https://github.com/dotnet/roslyn/issues/50053")] + public void PrivatelyImplementingInitOnlyProperty_ReferenceConversion() + { + string source = @" +var x = new DerivedType() { SomethingElse = 42 }; +System.Console.Write(x.SomethingElse); + +public interface ISomething { int Property { get; init; } } +public record BaseType : ISomething { int ISomething.Property { get; init; } } + +public record DerivedType : BaseType +{ + public int SomethingElse + { + get => ((ISomething)this).Property; + init => ((ISomething)this).Property = value; + } +} +"; + + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + // [ : BaseType::ISomething.set_Property] Cannot change initonly field outside its .ctor. + CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped); + } + + [Fact, WorkItem(50053, "https://github.com/dotnet/roslyn/issues/50053")] + public void PrivatelyImplementingInitOnlyProperty_BoxingConversion() + { + string source = @" +var x = new Type() { SomethingElse = 42 }; + +public interface ISomething { int Property { get; init; } } + +public struct Type : ISomething +{ + int ISomething.Property { get; init; } + + public int SomethingElse + { + get => throw null; + init => ((ISomething)this).Property = value; + } +} +"; + + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (13,17): error CS8852: Init-only property or indexer 'ISomething.Property' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. + // init => ((ISomething)this).Property = value; + Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "((ISomething)this).Property").WithArguments("ISomething.Property").WithLocation(13, 17) + ); + } + [Fact] public void OverridingInitOnlyProperty() { From 8d828c85608cf2b7de222b130d9cbefa8015f136 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 27 Jan 2021 17:48:17 -0800 Subject: [PATCH 2/2] Revert code changes --- .../CSharp/Portable/Binder/Binder.ValueChecks.cs | 11 ----------- .../Test/Semantic/Semantics/InitOnlyMemberTests.cs | 8 +++++--- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 4ab816f030270..2ca3f79498740 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1196,17 +1196,6 @@ bool isAllowedInitOnlySet(BoundExpression receiver) return true; } - while (receiver is BoundConversion boundConversion) - { - var conversion = boundConversion.Conversion; - if (!(conversion.IsIdentity || conversion.IsReference)) - { - return false; - } - - receiver = boundConversion.Operand; - } - // bad: other.InitOnlyProperty = ... if (!(receiver is BoundThisReference || receiver is BoundBaseReference)) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs index e2f129ccba9da..8ab4cf495f76a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs @@ -1681,9 +1681,11 @@ public int SomethingElse "; var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - // [ : BaseType::ISomething.set_Property] Cannot change initonly field outside its .ctor. - CompileAndVerify(comp, expectedOutput: "42", verify: Verification.Skipped); + comp.VerifyEmitDiagnostics( + // (13,17): error CS8852: Init-only property or indexer 'ISomething.Property' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. + // init => ((ISomething)this).Property = value; + Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "((ISomething)this).Property").WithArguments("ISomething.Property").WithLocation(13, 17) + ); } [Fact, WorkItem(50053, "https://github.com/dotnet/roslyn/issues/50053")]