-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make partial interface members virtual #77379
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Impl and testing seems good, we should just seek confirmation that we want to do this as a bug-fix level change, and figure out if we need to update the breaking change doc.
src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs
Outdated
Show resolved
Hide resolved
It is unfortunate that we have this behavior with already shipped features. Changing virtual-ness of interface members feels like a big change to me. #Closed |
src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs
Outdated
Show resolved
Hide resolved
Done with review pass (commit 3) #Resolved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo additional suggested tests and confirmation on moving forward with the break.
@RikkiGibson @AlekseyTs for another look, thanks |
Good to go from my perspective. #Closed |
@jjonescz It looks like there are conflicts that should be resolved #Closed |
@@ -794,7 +794,7 @@ partial C() { } | |||
} | |||
"""; | |||
CreateCompilation(source).VerifyDiagnostics( | |||
// (3,13): error CS9276: Partial member 'C.C()' must have a definition part. | |||
// (3,13): error CS9401: Partial member 'C.C()' must have a definition part. | |||
// partial C() { } | |||
Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(3, 13), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's cover other accessibility modifiers (it looks like we cover public
and private
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is placed on unchanged code but judging by the quoted text // (3,48): error CS0238: 'I.E' cannot be sealed because it is not an override
, I assume this comment was intended for InInterface_Sealed_Private
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct (the location), this is a brand new test, so I am not sure why you consider it an unchanged code. Even though I "dismissed" my comment in the first message of this thread, the second message is still relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a brand new test, so I am not sure why you consider it an unchanged code.
Sorry, I meant the location that this comment appears on GitHub and my CodeFlow is on unchanged test. I guess CodeFlow doesn't play nicely with merge commits.
src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs
Outdated
Show resolved
Hide resolved
using System; | ||
|
||
M(new C1()); | ||
M(new C2()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
static void validateEvent(EventSymbol e) | ||
{ | ||
Assert.False(e.IsAbstract); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// partial event System.Action E { add { } remove { } } | ||
Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(9, 33), | ||
// (9,33): error CS0102: The type 'C' already contains a definition for 'E' | ||
// partial event System.Action E { add { } remove { } } | ||
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(9, 33), | ||
// (11,13): error CS9278: Partial member 'C.C()' may not have multiple implementing declarations. | ||
// (11,13): error CS9403: Partial member 'C.C()' may not have multiple implementing declarations. | ||
// partial C() { } | ||
Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(11, 13), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what would you like to see tested in such scenarios. We already have test for the error that will occur "Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers." (That's tested for class, can extend that to interface if you want)
var source = $$""" | ||
partial interface I | ||
{ | ||
public static {{modifier}} partial int M(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""; | ||
var comp = CreateCompilation(source, targetFramework: TargetFramework.Net60) | ||
.VerifyDiagnostics( | ||
// (3,32): error CS0238: 'I.M()' cannot be sealed because it is not an override |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess same response as for events applies here - this is consistent with non-partial methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a second message on that thread, that is still relevant
{ | ||
// In C# 13 and earlier, interface partial members were not implicitly public and virtual and that is fixed only in C# 14 and later to avoid a breaking change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The breaking change email doesn't mention this aspect explicitly at all. It only talks about virtual-ness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The breaking change email doesn't mention this aspect explicitly at all. It only talks about virtual-ness.
Yes, I narrowed down the focus of the email because I think the virtual
ness is the most important part, it can lead to silent behavior breaks. The accessibility break leads to source breaks only, and it doesn't affect methods. The email has link to this doc which has all the details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but I do not find this response satisfactory. We can discuss offline in more details
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scheduled for LDM: dotnet/csharplang#9282
} | ||
|
||
[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/77346")] | ||
public void InInterface_NonVirtual_CSharp13_Private( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, will remove, thanks.
src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs
Outdated
Show resolved
Hide resolved
} | ||
|
||
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77346")] | ||
public void InInterface_StaticVirtual_CSharp13() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there isn't, I will merge the tests, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I cannot merge them because the combination langVersion=CSharp13
+ access=""
has a different behavior. But it's correct that InInterface_StaticVirtual(langVersion=CSharp13, access="public")
would have the same behavior as InInterface_StaticVirtual_CSharp13
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it hard to track differences in behavior across multiple "related" tests, hard to put back the entire matrix together and what/where the differences are in the matrix. It feels like it might be better to cover the entire matrix in one test, but adjust asserts to make expectations conditional based on the inputs. I think this way it will be easier to observe differences in behavior and when they happen. Consider restructuring tests this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you decide to pursue this suggestion, we can work on it offline together
// partial event System.Action E; | ||
Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "E").WithArguments("C.E").WithLocation(4, 33), | ||
// (4,33): error CS0102: The type 'C' already contains a definition for 'E' | ||
// partial event System.Action E; | ||
Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 33), | ||
// (5,33): error CS9277: Partial member 'C.F' may not have multiple defining declarations. | ||
// (5,33): error CS9402: Partial member 'C.F' may not have multiple defining declarations. | ||
// partial event System.Action F; | ||
Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "F").WithArguments("C.F").WithLocation(5, 33), | ||
// (5,33): error CS0102: The type 'C' already contains a definition for 'F' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, I cannot decipher where this comment was supposed to point to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, I cannot decipher where this comment was supposed to point to
For me, CodeFlow successfully places the comment on line 1013 in commit 6: Assert.Equal(Accessibility.Public, e.DeclaredAccessibility);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then yes, previously the event was implicitly private now it is implicitly public.
{ | ||
// In C# 13 and earlier, interface partial members were not implicitly public and virtual and that is fixed only in C# 14 and later to avoid a breaking change. | ||
bool newPartialBehavior = (mods & DeclarationModifiers.Partial) == 0 || ((CSharpParseOptions)syntaxNode.SyntaxTree.Options).LanguageVersion > LanguageVersion.CSharp13; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps changing the name of the local might improve things
***Introduced in Visual Studio 2022 version 17.15*** | ||
|
||
We have fixed [an inconsistency](https://github.com/dotnet/roslyn/issues/77346) | ||
where interface members would not be implicitly `virtual` and `public` if they were also marked `partial`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this description is unclear because accessibility for methods isn't affected by the change #Resolved
partial interface I | ||
{ | ||
public partial void M(); | ||
public partial void M() // implicitly virtual now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with review pass (commit 6) |
Fixes #77346.
The classic (not extended) partial methods are always private (hence also not virtual), and that is also true in interfaces. However, the extended partial methods feature allows partial methods to be public and virtual, but in interfaces, they are not implicitly public nor virtual unlike their non-partial counterparts which I think was just an oversight (all speclet wording I could find in the mentioned features supports that). Since partial properties and events are based on extended partial methods, they have the same bug. This PR fixes all that.