-
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
Support declaring operators + - ! ~ ++ -- true false * / % & | ^ << >> > < >= <=
in interfaces.
#20401
Support declaring operators + - ! ~ ++ -- true false * / % & | ^ << >> > < >= <=
in interfaces.
#20401
Conversation
…< >> > < >= <=``` in interfaces.
@@ -319,6 +320,36 @@ private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression ope | |||
} | |||
} | |||
|
|||
// Look in base interfaces, or effective interfaces for type parameters |
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.
A note above for the spec quote could be useful to indicate that it's no longer valid. #Resolved
|
||
foreach (NamedTypeSymbol @interface in interfaces) | ||
{ | ||
GetUserDefinedUnaryOperatorsFromType(@interface, kind, name, operators); |
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.
Why do we only look at the user defined operators for one base type at at time, where for interfaces we gather all user defined operators together? #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.
For simplicity, the overload resolution is going to do the right thing any way and each interface can define at most one unary operator of each kind. Tracking inheritance relationship in interfaces is not that trivial due to multiple inheritance. Effectively, the end result is going to be the same. #Resolved
var result = false; | ||
foreach (var v in values) | ||
{ | ||
result |= set.Add(v); |
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 a set returns true
only if the element was successfully added, wouldn't you expect AddAll
to only return true if all elements were successfully added?
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 am not trying to redefine the result, just adding a more efficient overload for an ImmutableArray. Either behavior would be fine, depending on consumer's needs. For the purposes of this PR, I do not care what is the result.
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.
Even if you don't use the result, I think the proper result behavior is to return true
if and only if all items were added successfully and false otherwise. That way others can use this helper and rely upon the result.
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 see the overload now, nevermind :) 👍
var result = false; | ||
foreach (var v in values) | ||
{ | ||
result |= set.Remove(v); |
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.
Same question about result behavior, as above.
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.
Same answer.
<data name="ERR_InterfacesCantContainOperators" xml:space="preserve"> | ||
<value>Interfaces cannot contain operators</value> | ||
<data name="ERR_InterfacesCantContainConversionOrEqualityOperators" xml:space="preserve"> | ||
<value>Interfaces cannot contain conversion, equality, or inequality operators</value> |
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.
Looking at test Operators_01
, it seems that inequality operators are supported. Maybe the error message is out-of-date? #Closed
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.
Looking at test Operators_01, it seems that inequality operators are supported. Maybe the error message is out-of-date?
This test doesn't test unsupported operators. Neither conversion, nor equality/inequality. #Closed
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 got confused thinking that inequality was <= and related...
In reply to: 123673420 [](ancestors = 123673420)
@@ -51,6 +51,8 @@ class Test1 : I1 | |||
|
|||
- Declaring static fields, auto-properties and field-like events (**protected** modifier is not allowed). | |||
|
|||
- Declaring operators ```+ - ! ~ ++ -- true false * / % & | ^ << >> > < >= <=``` in interfaces. |
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.
Is the intention to support equality later on? If not, maybe add a note as to why it is not allowed.
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.
Is the intention to support equality later on? If not, maybe add a note as to why it is not allowed.
This is a topic for LDM discussion. The list is meant to simply enumerate what is supported by the prototype without any reasoning, and it doesn't have to match the proposed spec.
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "+").WithArguments("IA.operator +(int, int)").WithLocation(4, 17), | ||
// (4,17): error CS0563: One of the parameters of a binary operator must be the containing type | ||
// int operator +(int aa, int bb); // CS0567 | ||
Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "+").WithLocation(4, 17) |
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 diagnostics offer solutions that will fail (because LangVer=7). Seems confusing.
Maybe some of the messages could include a required version ("Please use version X or greater"), conditional on LangVer?
Could be follow-up issue.
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 diagnostics offer solutions that will fail (because LangVer=7). Seems confusing.
Maybe some of the messages could include a required version ("Please use version X or greater"), conditional on LangVer?
No these are two unrelated errors and each has distinct fix. I do not intend to bundle them together now, or in the future. #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.
To clarify, we don't complain about language version because we complain about missing body. This approach is consistent with other kinds of non-virtual members in interfaces. Basically, if whatever you have is invalid regardless of the version, we might not complain about version mismatch as a measure of suppressing the noise. #Resolved
{ | ||
Binder.CheckFeatureAvailability(node, MessageID.IDS_DefaultInterfaceImplementation, diagnostics); | ||
} | ||
} |
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.
Consider extracting a helper method for use here and in BinaryOperatorOverloadResolution
. #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.
Consider extracting a helper method for use here and in BinaryOperatorOverloadResolution.
Done. #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
case MethodKind.UserDefinedOperator: | ||
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainOperators, member.Locations[0]); | ||
if (meth.Name == WellKnownMemberNames.EqualityOperatorName || meth.Name == WellKnownMemberNames.InequalityOperatorName) |
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.
Consider extracting name checks to helper method since the same checks occur in multiple places.
CompilationReference compilationReference = compilation1.ToMetadataReference(); | ||
MetadataReference metadataReference = compilation1.EmitToImageReference(); | ||
|
||
|
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.
Are we testing shadowing does not depend on the order of base interfaces in the derived interface declaration?
interface IA
{
public static IA operator+(IA x, IB y) => null;
}
interface IB : IA
{
public static IA operator+(IB x, IA y) => null;
}
interface IC1 : IA, IB { }
interface IC2 : IB, IA { }
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'll check and add an explicit test for a scenario like that in the next PR.
@dotnet/roslyn-compiler Please review.