Skip to content
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

Conversation

AlekseyTs
Copy link
Contributor

@dotnet/roslyn-compiler Please review.

@@ -319,6 +320,36 @@ private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression ope
}
}

// Look in base interfaces, or effective interfaces for type parameters
Copy link
Member

@agocke agocke Jun 22, 2017

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);
Copy link
Member

@agocke agocke Jun 22, 2017

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

Copy link
Contributor Author

@AlekseyTs AlekseyTs Jun 22, 2017

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);
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Member

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);
Copy link
Member

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.

Copy link
Contributor Author

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>
Copy link
Member

@jcouv jcouv Jun 23, 2017

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

Copy link
Contributor Author

@AlekseyTs AlekseyTs Jun 23, 2017

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

Copy link
Member

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.
Copy link
Member

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.

Copy link
Contributor Author

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)
Copy link
Member

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.

Copy link
Contributor Author

@AlekseyTs AlekseyTs Jun 23, 2017

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

Copy link
Contributor Author

@AlekseyTs AlekseyTs Jun 23, 2017

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);
}
}
Copy link
Member

@cston cston Jun 23, 2017

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

Copy link
Contributor Author

@AlekseyTs AlekseyTs Jun 23, 2017

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

Copy link
Member

@jcouv jcouv left a 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)
Copy link
Member

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();


Copy link
Member

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 { }

Copy link
Contributor Author

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.

@AlekseyTs AlekseyTs merged commit 12c5fec into dotnet:features/DefaultInterfaceImplementation Jun 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants