-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Diagnostics; | ||
using Microsoft.CodeAnalysis.Collections; | ||
using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Roslyn.Utilities; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
|
||
namespace Microsoft.CodeAnalysis.CSharp | ||
{ | ||
|
@@ -284,6 +287,8 @@ private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression ope | |
// SPEC: operators is the set provided by the direct base class of T0, or the effective | ||
// SPEC: base class of T0 if T0 is a type parameter. | ||
|
||
// PROTOTYPE(DefaultInterfaceImplementation): The spec quote should be adjusted to cover operators from interfaces as well. | ||
|
||
TypeSymbol type0 = operand.Type.StrippedType(); | ||
|
||
// Searching for user-defined operators is expensive; let's take an early out if we can. | ||
|
@@ -319,6 +324,51 @@ 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 commentThe 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 |
||
if (!hadApplicableCandidates) | ||
{ | ||
ImmutableArray<NamedTypeSymbol> interfaces = default; | ||
if (type0.IsInterfaceType()) | ||
{ | ||
interfaces = type0.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); | ||
} | ||
else if (type0.IsTypeParameter()) | ||
{ | ||
interfaces = ((TypeParameterSymbol)type0).AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); | ||
} | ||
|
||
if (!interfaces.IsDefaultOrEmpty) | ||
{ | ||
var shadowedInterfaces = PooledHashSet<NamedTypeSymbol>.GetInstance(); | ||
var resultsFromInterface = ArrayBuilder<UnaryOperatorAnalysisResult>.GetInstance(); | ||
results.Clear(); | ||
|
||
foreach (NamedTypeSymbol @interface in interfaces) | ||
{ | ||
if (shadowedInterfaces.Contains(@interface)) | ||
{ | ||
// this interface is "shadowed" by a derived interface | ||
continue; | ||
} | ||
|
||
operators.Clear(); | ||
resultsFromInterface.Clear(); | ||
GetUserDefinedUnaryOperatorsFromType(@interface, kind, name, operators); | ||
if (CandidateOperators(operators, operand, resultsFromInterface, ref useSiteDiagnostics)) | ||
{ | ||
hadApplicableCandidates = true; | ||
results.AddRange(resultsFromInterface); | ||
|
||
// this interface "shadows" all its base interfaces | ||
shadowedInterfaces.AddAll(@interface.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)); | ||
} | ||
} | ||
|
||
shadowedInterfaces.Free(); | ||
resultsFromInterface.Free(); | ||
} | ||
} | ||
|
||
operators.Free(); | ||
|
||
return hadApplicableCandidates; | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1574,8 +1574,8 @@ If such a class is used as a base class and if the deriving class defines a dest | |
<data name="ERR_BadShiftOperatorSignature" xml:space="preserve"> | ||
<value>The first operand of an overloaded shift operator must have the same type as the containing type, and the type of the second operand must be int</value> | ||
</data> | ||
<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 commentThe reason will be displayed to describe this comment to others. Learn more. Looking at test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 commentThe 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) |
||
</data> | ||
<data name="ERR_StructsCantContainDefaultConstructor" xml:space="preserve"> | ||
<value>Structs cannot contain explicit parameterless constructors</value> | ||
|
@@ -2510,7 +2510,7 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep | |
<value>Could not find '{0}' specified for Main method</value> | ||
</data> | ||
<data name="ERR_MainClassNotClass" xml:space="preserve"> | ||
<value>'{0}' specified for Main method must be a valid non-generic class or struct or interface</value> | ||
<value>'{0}' specified for Main method must be a non-generic class, struct, or interface</value> | ||
</data> | ||
<data name="ERR_NoMainInClass" xml:space="preserve"> | ||
<value>'{0}' does not have a suitable static Main method</value> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2711,8 +2711,13 @@ private static void CheckInterfaceMember(Symbol member, DiagnosticBag diagnostic | |
} | ||
break; | ||
case MethodKind.Conversion: | ||
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, member.Locations[0]); | ||
break; | ||
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 commentThe 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. |
||
{ | ||
diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, member.Locations[0]); | ||
} | ||
break; | ||
case MethodKind.Destructor: | ||
diagnostics.Add(ErrorCode.ERR_OnlyClassesCanContainDestructors, member.Locations[0]); | ||
|
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.
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.