diff --git a/docs/features/DefaultInterfaceImplementation.md b/docs/features/DefaultInterfaceImplementation.md index 173f2931e9b3d..c64ba01fa798c 100644 --- a/docs/features/DefaultInterfaceImplementation.md +++ b/docs/features/DefaultInterfaceImplementation.md @@ -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. + **Open issues and work items** are tracked in https://github.com/dotnet/roslyn/issues/17952. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 4a1c31ff9335e..fe57040cc6f4f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -1083,17 +1083,29 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution(BinaryOper resultKind = possiblyBest.HasValue ? LookupResultKind.Viable : LookupResultKind.Empty; } - if (possiblyBest.HasValue && - (object)possiblyBest.Signature.Method != null) + if (possiblyBest.HasValue) { - Symbol symbol = possiblyBest.Signature.Method; - ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: false); + ReportObsoleteAndFeatureAvailabilityDiagnostics(possiblyBest.Signature.Method, node, diagnostics); } result.Free(); return possiblyBest; } + private void ReportObsoleteAndFeatureAvailabilityDiagnostics(MethodSymbol operatorMethod, CSharpSyntaxNode node, DiagnosticBag diagnostics) + { + if ((object)operatorMethod != null) + { + ReportDiagnosticsIfObsolete(diagnostics, operatorMethod, node, hasBaseReceiver: false); + + if (operatorMethod.ContainingType.IsInterface && + operatorMethod.ContainingModule != Compilation.SourceModule) + { + Binder.CheckFeatureAvailability(node, MessageID.IDS_DefaultInterfaceImplementation, diagnostics); + } + } + } + private bool IsDefaultLiteralAllowedInBinaryOperator(BinaryOperatorKind kind, BoundExpression left, BoundExpression right) { bool isEquality = kind == BinaryOperatorKind.Equal || kind == BinaryOperatorKind.NotEqual; @@ -1169,11 +1181,9 @@ private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution( resultKind = possiblyBest.HasValue ? LookupResultKind.Viable : LookupResultKind.Empty; } - if (possiblyBest.HasValue && - (object)possiblyBest.Signature.Method != null) + if (possiblyBest.HasValue) { - Symbol symbol = possiblyBest.Signature.Method; - ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver: false); + ReportObsoleteAndFeatureAvailabilityDiagnostics(possiblyBest.Signature.Method, node, diagnostics); } result.Free(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs index f825da442b016..398d0d1ef35c3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs @@ -1,11 +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 { @@ -39,7 +41,41 @@ public void BinaryOperatorOverloadResolution(BinaryOperatorKind kind, BoundExpre // SPEC: The set of candidate user-defined operators provided by the types (if any) of x and y for the // SPEC operation operator op(x, y) is determined. - bool hadUserDefinedCandidate = GetUserDefinedOperators(underlyingKind, left, right, result.Results, ref useSiteDiagnostics); + TypeSymbol leftOperatorSourceOpt = left.Type?.StrippedType(); + TypeSymbol rightOperatorSourceOpt = right.Type?.StrippedType(); + bool leftSourceIsInterface = leftOperatorSourceOpt?.IsInterfaceType() == true; + bool rightSourceIsInterface = rightOperatorSourceOpt?.IsInterfaceType() == true; + BinaryOperatorOverloadResolutionResult resultFromNonInterfaces = null; + + // The following is a slight rewording of the specification to emphasize that not all + // operands of a binary operation need to have a type. + + // TODO (tomat): The spec needs to be updated to use identity conversion instead of type equality. + + // Spec 7.3.4 Binary operator overload resolution: + // An operation of the form x op y, where op is an overloadable binary operator is processed as follows: + // The set of candidate user-defined operators provided by the types (if any) of x and y for the + // operation operator op(x, y) is determined. The set consists of the union of the candidate operators + // provided by the type of x (if any) and the candidate operators provided by the type of y (if any), + // each determined using the rules of 7.3.5. Candidate operators only occur in the combined set once. + + bool leftHadUserDefinedCandidate = false; + bool rightHadUserDefinedCandidate = false; + + // In order to preserve backward compatibility, at first we ignore interface sources. + + if (!leftSourceIsInterface) + { + leftHadUserDefinedCandidate = GetUserDefinedOperators(kind, leftOperatorSourceOpt, left, right, result.Results, ref useSiteDiagnostics); + } + + if (!rightSourceIsInterface && rightOperatorSourceOpt != leftOperatorSourceOpt) + { + var rightOperators = ArrayBuilder.GetInstance(); + rightHadUserDefinedCandidate = GetUserDefinedOperators(kind, rightOperatorSourceOpt, left, right, rightOperators, ref useSiteDiagnostics); + AddDistinctOperators(result.Results, rightOperators); + rightOperators.Free(); + } // SPEC: If the set of candidate user-defined operators is not empty, then this becomes the set of candidate // SPEC: operators for the operation. Otherwise, the predefined binary operator op implementations, including @@ -60,18 +96,181 @@ public void BinaryOperatorOverloadResolution(BinaryOperatorKind kind, BoundExpre // // Roslyn matches the specification and takes the break from the native compiler. - if (!hadUserDefinedCandidate) + if (!leftHadUserDefinedCandidate && !rightHadUserDefinedCandidate) { + Debug.Assert(result.Results.Count == 0); result.Results.Clear(); GetAllBuiltInOperators(kind, left, right, result.Results, ref useSiteDiagnostics); } - + else if (kind != BinaryOperatorKind.Equal && kind != BinaryOperatorKind.NotEqual && + ((!leftHadUserDefinedCandidate && + ShouldGetUserDefinedBinaryOperatorsFromInterfaces(leftOperatorSourceOpt, leftSourceIsInterface, ref useSiteDiagnostics)) || + (!rightHadUserDefinedCandidate && leftOperatorSourceOpt != rightOperatorSourceOpt && + ShouldGetUserDefinedBinaryOperatorsFromInterfaces(rightOperatorSourceOpt, rightSourceIsInterface, ref useSiteDiagnostics)))) + { + Debug.Assert(result.Results.Any(r => r.IsValid)); + // Copy candidates to avoid repeating lookup in case we need to add operators from interfaces + resultFromNonInterfaces = BinaryOperatorOverloadResolutionResult.GetInstance(); + resultFromNonInterfaces.Results.AddRange(result.Results); + } + // SPEC: The overload resolution rules of 7.5.3 are applied to the set of candidate operators to select the best // SPEC: operator with respect to the argument list (x, y), and this operator becomes the result of the overload // SPEC: resolution process. If overload resolution fails to select a single best operator, a binding-time // SPEC: error occurs. BinaryOperatorOverloadResolution(left, right, result, ref useSiteDiagnostics); + + // If resolution failed, try with interface sources + if (!result.Best.HasValue && + (!leftHadUserDefinedCandidate || !rightHadUserDefinedCandidate) && + kind != BinaryOperatorKind.Equal && kind != BinaryOperatorKind.NotEqual) + { + var resultFromInterfaces = BinaryOperatorOverloadResolutionResult.GetInstance(); + bool leftHadUserDefinedCandidateFromInterfaces = false; + bool rightHadUserDefinedCandidateFromInterfaces = false; + string name = OperatorFacts.BinaryOperatorNameFromOperatorKind(kind); + var lookedInInterfaces = PooledDictionary.GetInstance(); + + if (!leftHadUserDefinedCandidate) + { + leftHadUserDefinedCandidateFromInterfaces = GetUserDefinedBinaryOperatorsFromInterfaces(kind, name, + leftOperatorSourceOpt, leftSourceIsInterface, left, right, ref useSiteDiagnostics, lookedInInterfaces, resultFromInterfaces.Results); + } + + if (!rightHadUserDefinedCandidate && leftOperatorSourceOpt != rightOperatorSourceOpt) + { + var rightOperators = ArrayBuilder.GetInstance(); + rightHadUserDefinedCandidateFromInterfaces = GetUserDefinedBinaryOperatorsFromInterfaces(kind, name, + rightOperatorSourceOpt, rightSourceIsInterface, left, right, ref useSiteDiagnostics, lookedInInterfaces, rightOperators); + + AddDistinctOperators(resultFromInterfaces.Results, rightOperators); + rightOperators.Free(); + } + + Debug.Assert(resultFromNonInterfaces != null || + lookedInInterfaces.Count == 0 || + (!leftHadUserDefinedCandidate && !rightHadUserDefinedCandidate)); + + if (leftHadUserDefinedCandidateFromInterfaces || rightHadUserDefinedCandidateFromInterfaces) + { + result.Clear(); + + if (resultFromNonInterfaces != null) + { + result.Results.AddRange(resultFromNonInterfaces.Results); + } + + result.Results.AddRange(resultFromInterfaces.Results); + BinaryOperatorOverloadResolution(left, right, result, ref useSiteDiagnostics); + } + + resultFromInterfaces.Free(); + lookedInInterfaces.Free(); + } + + if (resultFromNonInterfaces != null) + { + resultFromNonInterfaces.Free(); + } + } + + private static bool ShouldGetUserDefinedBinaryOperatorsFromInterfaces( + TypeSymbol operatorSourceOpt, bool sourceIsInterface, + ref HashSet useSiteDiagnostics) + { + return (object)operatorSourceOpt != null && + (sourceIsInterface || + (operatorSourceOpt.IsTypeParameter() && + !((TypeParameterSymbol)operatorSourceOpt).AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics).IsEmpty)); + } + + private bool GetUserDefinedBinaryOperatorsFromInterfaces(BinaryOperatorKind kind, string name, + TypeSymbol operatorSourceOpt, bool sourceIsInterface, + BoundExpression left, BoundExpression right, ref HashSet useSiteDiagnostics, + Dictionary lookedInInterfaces, ArrayBuilder candidates) + { + Debug.Assert(candidates.Count == 0); + + if ((object)operatorSourceOpt == null) + { + return false; + } + + bool hadUserDefinedCandidateFromInterfaces = false; + ImmutableArray interfaces = default; + + if (sourceIsInterface) + { + + if (!lookedInInterfaces.TryGetValue(operatorSourceOpt, out _)) + { + var operators = ArrayBuilder.GetInstance(); + GetUserDefinedBinaryOperatorsFromType((NamedTypeSymbol)operatorSourceOpt, kind, name, operators); + hadUserDefinedCandidateFromInterfaces = CandidateOperators(operators, left, right, candidates, ref useSiteDiagnostics); + operators.Free(); + Debug.Assert(hadUserDefinedCandidateFromInterfaces == candidates.Any(r => r.IsValid)); + + lookedInInterfaces.Add(operatorSourceOpt, hadUserDefinedCandidateFromInterfaces); + if (!hadUserDefinedCandidateFromInterfaces) + { + candidates.Clear(); + interfaces = operatorSourceOpt.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); + } + } + } + else if (operatorSourceOpt.IsTypeParameter()) + { + interfaces = ((TypeParameterSymbol)operatorSourceOpt).AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); + } + + if (!interfaces.IsDefaultOrEmpty) + { + var operators = ArrayBuilder.GetInstance(); + var results = ArrayBuilder.GetInstance(); + var shadowedInterfaces = PooledHashSet.GetInstance(); + + foreach (NamedTypeSymbol @interface in interfaces) + { + if (shadowedInterfaces.Contains(@interface)) + { + // this interface is "shadowed" by a derived interface + continue; + } + + if (lookedInInterfaces.TryGetValue(@interface, out bool hadUserDefinedCandidate)) + { + if (hadUserDefinedCandidate) + { + // this interface "shadows" all its base interfaces + shadowedInterfaces.AddAll(@interface.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)); + } + + // no need to perform another lookup in this interface + continue; + } + + operators.Clear(); + results.Clear(); + GetUserDefinedBinaryOperatorsFromType(@interface, kind, name, operators); + hadUserDefinedCandidate = CandidateOperators(operators, left, right, results, ref useSiteDiagnostics); + Debug.Assert(hadUserDefinedCandidate == results.Any(r => r.IsValid)); + lookedInInterfaces.Add(@interface, hadUserDefinedCandidate); + if (hadUserDefinedCandidate) + { + hadUserDefinedCandidateFromInterfaces = true; + candidates.AddRange(results); + // this interface "shadows" all its base interfaces + shadowedInterfaces.AddAll(@interface.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)); + } + } + + operators.Free(); + results.Free(); + shadowedInterfaces.Free(); + } + + return hadUserDefinedCandidateFromInterfaces; } private void AddDelegateOperation(BinaryOperatorKind kind, TypeSymbol delegateType, @@ -544,67 +743,6 @@ private bool CandidateOperators( return hadApplicableCandidate; } - // Returns an analysis of every matching user-defined binary operator, including whether the - // operator is applicable or not. - - private bool GetUserDefinedOperators( - BinaryOperatorKind kind, - BoundExpression left, - BoundExpression right, - ArrayBuilder results, - ref HashSet useSiteDiagnostics) - { - Debug.Assert(left != null); - Debug.Assert(right != null); - - // The following is a slight rewording of the specification to emphasize that not all - // operands of a binary operation need to have a type. - - // TODO (tomat): The spec needs to be updated to use identity conversion instead of type equality. - - // Spec 7.3.4 Binary operator overload resolution: - // An operation of the form x op y, where op is an overloadable binary operator is processed as follows: - // The set of candidate user-defined operators provided by the types (if any) of x and y for the - // operation operator op(x, y) is determined. The set consists of the union of the candidate operators - // provided by the type of x (if any) and the candidate operators provided by the type of y (if any), - // each determined using the rules of 7.3.5. Candidate operators only occur in the combined set once. - - var operators = ArrayBuilder.GetInstance(); - TypeSymbol leftType = left.Type; - TypeSymbol strippedLeftType = leftType?.StrippedType(); - - bool hadApplicableCandidate = false; - - if ((object)strippedLeftType != null && !OperatorFacts.DefinitelyHasNoUserDefinedOperators(strippedLeftType)) - { - hadApplicableCandidate = GetUserDefinedOperators(kind, strippedLeftType, left, right, operators, ref useSiteDiagnostics); - if (!hadApplicableCandidate) - { - operators.Clear(); - } - } - - TypeSymbol rightType = right.Type; - TypeSymbol strippedRightType = rightType?.StrippedType(); - if ((object)strippedRightType != null && !strippedRightType.Equals(strippedLeftType) && - !OperatorFacts.DefinitelyHasNoUserDefinedOperators(strippedRightType)) - { - var rightOperators = ArrayBuilder.GetInstance(); - hadApplicableCandidate |= GetUserDefinedOperators(kind, strippedRightType, left, right, rightOperators, ref useSiteDiagnostics); - AddDistinctOperators(operators, rightOperators); - rightOperators.Free(); - } - - if (hadApplicableCandidate) - { - results.AddRange(operators); - } - - operators.Free(); - - return hadApplicableCandidate; - } - private static void AddDistinctOperators(ArrayBuilder result, ArrayBuilder additionalOperators) { int initialCount = result.Count; @@ -646,6 +784,12 @@ private bool GetUserDefinedOperators( ArrayBuilder results, ref HashSet useSiteDiagnostics) { + Debug.Assert(results.Count == 0); + if ((object)type0 == null || OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0)) + { + return false; + } + // Spec 7.3.5 Candidate user-defined operators // SPEC: Given a type T and an operation operator op(A), where op is an overloadable // SPEC: operator and A is an argument list, the set of candidate user-defined operators @@ -693,6 +837,7 @@ private bool GetUserDefinedOperators( operators.Free(); + Debug.Assert(hadApplicableCandidates == results.Any(r => r.IsValid)); return hadApplicableCandidates; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolutionResult.cs index 40124a80fbac7..e393ca3c60a03 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolutionResult.cs @@ -118,10 +118,15 @@ public static BinaryOperatorOverloadResolutionResult GetInstance() public void Free() { - this.Results.Clear(); + Clear(); Pool.Free(this); } + public void Clear() + { + this.Results.Clear(); + } + public static readonly ObjectPool Pool = CreatePool(); private static ObjectPool CreatePool() diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs index 1fd3f3e3b1b86..e0b33aef56caf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/OperatorFacts.cs @@ -19,6 +19,7 @@ public static bool DefinitelyHasNoUserDefinedOperators(TypeSymbol type) case TypeKind.Struct: case TypeKind.Class: case TypeKind.TypeParameter: + case TypeKind.Interface: break; default: return true; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs index 59ea78a6be6d3..2e8bc0e7f1e8f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs @@ -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 + if (!hadApplicableCandidates) + { + ImmutableArray 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.GetInstance(); + var resultsFromInterface = ArrayBuilder.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; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 9d5b6708dc0b6..3fbe5a847070e 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -5426,20 +5426,20 @@ internal static string ERR_InterfacesCantContainConstructors { } /// - /// Looks up a localized string similar to Interfaces cannot contain instance fields. + /// Looks up a localized string similar to Interfaces cannot contain conversion, equality, or inequality operators. /// - internal static string ERR_InterfacesCantContainFields { + internal static string ERR_InterfacesCantContainConversionOrEqualityOperators { get { - return ResourceManager.GetString("ERR_InterfacesCantContainFields", resourceCulture); + return ResourceManager.GetString("ERR_InterfacesCantContainConversionOrEqualityOperators", resourceCulture); } } /// - /// Looks up a localized string similar to Interfaces cannot contain operators. + /// Looks up a localized string similar to Interfaces cannot contain instance fields. /// - internal static string ERR_InterfacesCantContainOperators { + internal static string ERR_InterfacesCantContainFields { get { - return ResourceManager.GetString("ERR_InterfacesCantContainOperators", resourceCulture); + return ResourceManager.GetString("ERR_InterfacesCantContainFields", resourceCulture); } } @@ -5975,7 +5975,7 @@ internal static string ERR_MainClassIsImport { } /// - /// Looks up a localized string similar to '{0}' specified for Main method must be a valid non-generic class or struct or interface. + /// Looks up a localized string similar to '{0}' specified for Main method must be a non-generic class, struct, or interface. /// internal static string ERR_MainClassNotClass { get { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 3373e4f5cb0c7..3e23c3ecbb5c8 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -1574,8 +1574,8 @@ If such a class is used as a base class and if the deriving class defines a dest 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 - - Interfaces cannot contain operators + + Interfaces cannot contain conversion, equality, or inequality operators Structs cannot contain explicit parameterless constructors @@ -2510,7 +2510,7 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Could not find '{0}' specified for Main method - '{0}' specified for Main method must be a valid non-generic class or struct or interface + '{0}' specified for Main method must be a non-generic class, struct, or interface '{0}' does not have a suitable static Main method diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 941e73e747e9a..9aea909cc5a58 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -381,7 +381,7 @@ internal enum ErrorCode ERR_BadUnaryOperatorSignature = 562, ERR_BadBinaryOperatorSignature = 563, ERR_BadShiftOperatorSignature = 564, - ERR_InterfacesCantContainOperators = 567, + ERR_InterfacesCantContainConversionOrEqualityOperators = 567, ERR_StructsCantContainDefaultConstructor = 568, ERR_CantOverrideBogusMethod = 569, ERR_BindToBogus = 570, diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs index 60a1c5084f49d..7ed469023e18e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_Warnings.cs @@ -331,7 +331,11 @@ private static bool ConvertedHasEqual(BinaryOperatorKind oldOperatorKind, BoundN var conv = (BoundConversion)node; if (conv.ExplicitCastInCode) return false; NamedTypeSymbol nt = conv.Operand.Type as NamedTypeSymbol; - if ((object)nt == null || !nt.IsReferenceType) return false; + if ((object)nt == null || !nt.IsReferenceType || nt.IsInterface) + { + return false; + } + string opName = (oldOperatorKind == BinaryOperatorKind.ObjectEqual) ? WellKnownMemberNames.EqualityOperatorName : WellKnownMemberNames.InequalityOperatorName; for (var t = nt; (object)t != null; t = t.BaseTypeNoUseSiteDiagnostics) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index a61d0b02c836d..5f855f9a987c4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -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) + { + diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, member.Locations[0]); + } break; case MethodKind.Destructor: diagnostics.Add(ErrorCode.ERR_OnlyClassesCanContainDestructors, member.Locations[0]); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs index c7002900f0113..3eb7e239ff442 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs @@ -45,6 +45,11 @@ private SourceUserDefinedOperatorSymbol( { CheckForBlockAndExpressionBody( syntax.Body, syntax.ExpressionBody, syntax, diagnostics); + + if (name != WellKnownMemberNames.EqualityOperatorName && name != WellKnownMemberNames.InequalityOperatorName) + { + CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: syntax.Body != null || syntax.ExpressionBody != null, diagnostics: diagnostics); + } } internal new OperatorDeclarationSyntax GetSyntax() diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 9a6a281e4cd2c..a9a78e5112132 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -57,9 +57,10 @@ protected SourceUserDefinedOperatorSymbolBase( this.MakeFlags(methodKind, declarationModifiers, returnsVoid: false, isExtensionMethod: false); - if (this.ContainingType.IsInterface) + if (this.ContainingType.IsInterface && + (methodKind == MethodKind.Conversion || name == WellKnownMemberNames.EqualityOperatorName || name == WellKnownMemberNames.InequalityOperatorName)) { - // If we have an operator in an interface, we already have reported that fact as + // If we have a conversion or equality/inequality operator in an interface, we already have reported that fact as // an error. No need to cascade the error further. return; } @@ -160,9 +161,11 @@ protected override void MethodChecks(DiagnosticBag diagnostics) this.SetReturnsVoid(_lazyReturnType.SpecialType == SpecialType.System_Void); - // If we have an operator in an interface or static class then we already - // have reported that fact as an error. No need to cascade the error further. - if (this.ContainingType.IsInterfaceType() || this.ContainingType.IsStatic) + // If we have a conversion/equality/inequality operator in an interface or static class then we already + // have reported that fact as an error. No need to cascade the error further. + if ((this.ContainingType.IsInterfaceType() && + (MethodKind == MethodKind.Conversion || Name == WellKnownMemberNames.EqualityOperatorName || Name == WellKnownMemberNames.InequalityOperatorName)) || + this.ContainingType.IsStatic) { return; } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 2f4e8dcbc29d6..fb1449556fef4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; @@ -994,6 +995,7 @@ private static Symbol FindMostSpecificImplementation(Symbol interfaceMember, conflictingImplementation1 = null; conflictingImplementation2 = null; Symbol implementation = null; + PooledHashSet shadowedInterfaces = null; foreach (var interfaceType in implementingType.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { @@ -1004,8 +1006,10 @@ private static Symbol FindMostSpecificImplementation(Symbol interfaceMember, if ((object)implementation == null) { implementation = candidate; + shadowedInterfaces = PooledHashSet.GetInstance(); + shadowedInterfaces.AddAll(implementation.ContainingType.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)); } - else if(!implementation.ContainingType.ImplementsInterface(interfaceType, ref useSiteDiagnostics)) + else if(!shadowedInterfaces.Contains(interfaceType)) { // we have a conflict conflictingImplementation1 = implementation; @@ -1016,6 +1020,11 @@ private static Symbol FindMostSpecificImplementation(Symbol interfaceMember, } } + if (shadowedInterfaces != null) + { + shadowedInterfaces.Free(); + } + return implementation; } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs index 8d91637f758d6..70af49f0408b8 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EntryPointTests.cs @@ -251,7 +251,7 @@ namespace N { namespace M { } } "; var compilation = CreateStandardCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("N.M")); compilation.VerifyDiagnostics( - // (2,25): error CS1556: 'N.M' specified for Main method must be a valid non-generic class or struct or interface + // (2,25): error CS1556: 'N.M' specified for Main method must be a non-generic class, struct, or interface Diagnostic(ErrorCode.ERR_MainClassNotClass, "M").WithArguments("N.M")); } @@ -269,7 +269,7 @@ public static void Main() { } "; var compilation = CreateStandardCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("C.D")); compilation.VerifyDiagnostics( - // (4,12): error CS1556: 'C.D' specified for Main method must be a valid non-generic class or struct or interface + // (4,12): error CS1556: 'C.D' specified for Main method must be a non-generic class, struct, or interface Diagnostic(ErrorCode.ERR_MainClassNotClass, "D").WithArguments("C.D")); } @@ -343,7 +343,7 @@ public static class E // Dev10 reports: CS1555: Could not find 'D.DD' specified for Main method compilation = CreateStandardCompilation(cs, options: TestOptions.ReleaseExe.WithMainTypeName("D.DD")); compilation.VerifyDiagnostics( - // (18,25): error CS1556: 'D.DD' specified for Main method must be a valid non-generic class or struct or interface + // (18,25): error CS1556: 'D.DD' specified for Main method must be a non-generic class, struct, or interface Diagnostic(ErrorCode.ERR_MainClassNotClass, "DD").WithArguments("D.DD")); } @@ -420,7 +420,7 @@ class C }"; // Dev10 reports CS1555: Could not find 'A.B.C' specified for Main method CreateStandardCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("A.B.C")).VerifyDiagnostics( - // (14,11): error CS1556: 'A.B.C' specified for Main method must be a valid non-generic class or struct or interface + // (14,11): error CS1556: 'A.B.C' specified for Main method must be a non-generic class, struct, or interface Diagnostic(ErrorCode.ERR_MainClassNotClass, "C").WithArguments("A.B.C")); } @@ -465,7 +465,7 @@ static void Main() { } "; var compilation = CreateStandardCompilation(source, options: TestOptions.ReleaseExe.WithMainTypeName("C")); compilation.VerifyDiagnostics( - // (7,7): error CS1556: 'C' specified for Main method must be a valid non-generic class or struct or interface + // (7,7): error CS1556: 'C' specified for Main method must be a non-generic class, struct, or interface Diagnostic(ErrorCode.ERR_MainClassNotClass, "C").WithArguments("C")); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index 77b72b5dfcdea..adf7b9b067456 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -30867,5 +30867,2738 @@ static void Main() CompileAndVerify(compilation1, expectedOutput: "I2.Main"); } + [Fact] + public void Operators_01() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator +(I1 x) + { + System.Console.WriteLine(""+""); + return x; + } + + public static I1 operator -(I1 x) + { + System.Console.WriteLine(""-""); + return x; + } + + public static I1 operator !(I1 x) + { + System.Console.WriteLine(""!""); + return x; + } + + public static I1 operator ~(I1 x) + { + System.Console.WriteLine(""~""); + return x; + } + + public static I1 operator ++(I1 x) + { + System.Console.WriteLine(""++""); + return x; + } + + public static I1 operator --(I1 x) + { + System.Console.WriteLine(""--""); + return x; + } + + public static bool operator true(I1 x) + { + System.Console.WriteLine(""true""); + return true; + } + + public static bool operator false(I1 x) + { + System.Console.WriteLine(""false""); + return false; + } + + public static I1 operator +(I1 x, I1 y) + { + System.Console.WriteLine(""+2""); + return x; + } + + public static I1 operator -(I1 x, I1 y) + { + System.Console.WriteLine(""-2""); + return x; + } + + public static I1 operator *(I1 x, I1 y) + { + System.Console.WriteLine(""*""); + return x; + } + + public static I1 operator /(I1 x, I1 y) + { + System.Console.WriteLine(""/""); + return x; + } + + public static I1 operator %(I1 x, I1 y) + { + System.Console.WriteLine(""%""); + return x; + } + + public static I1 operator &(I1 x, I1 y) + { + System.Console.WriteLine(""&""); + return x; + } + + public static I1 operator |(I1 x, I1 y) + { + System.Console.WriteLine(""|""); + return x; + } + + public static I1 operator ^(I1 x, I1 y) + { + System.Console.WriteLine(""^""); + return x; + } + + public static I1 operator <<(I1 x, int y) + { + System.Console.WriteLine(""<<""); + return x; + } + + public static I1 operator >>(I1 x, int y) + { + System.Console.WriteLine("">>""); + return x; + } + + public static I1 operator >(I1 x, I1 y) + { + System.Console.WriteLine("">""); + return x; + } + + public static I1 operator <(I1 x, I1 y) + { + System.Console.WriteLine(""<""); + return x; + } + + public static I1 operator >=(I1 x, I1 y) + { + System.Console.WriteLine("">=""); + return x; + } + + public static I1 operator <=(I1 x, I1 y) + { + System.Console.WriteLine(""<=""); + return x; + } +} +"; + + var source2 = +@" +class Test2 : I1 +{ + static void Main() + { + I1 x = new Test2(); + I1 y = new Test2(); + + x = +x; + x = -x; + x = !x; + x = ~x; + x = ++x; + x = x--; + + x = x + y; + x = x - y; + x = x * y; + x = x / y; + x = x % y; + if (x && y) { } + x = x | y; + x = x ^ y; + x = x << 1; + x = x >> 2; + x = x > y; + x = x < y; + x = x >= y; + x = x <= y; + } +} +"; + + var expectedOutput = +@" ++ +- +! +~ +++ +-- ++2 +-2 +* +/ +% +false +& +true +| +^ +<< +>> +> +< +>= +<= +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + + // Avoid sharing mscorlib symbols with other tests since we are about to change + // RuntimeSupportsDefaultInterfaceImplementation property for it. + var standardRefs = StandardReferencesWithoutSharingCachedSymbols; + + var compilation4 = CreateCompilation(source2, standardRefs, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + compilation4.Assembly.RuntimeSupportsDefaultInterfaceImplementation = false; + compilation4 = compilation4.AddReferences(compilationReference); + Assert.False(compilation4.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation4.VerifyDiagnostics(); + CompileAndVerify(compilation4, expectedOutput: expectedOutput); + + var compilation5 = CreateCompilation(source2, standardRefs.Concat(new[] { metadataReference }), options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.False(compilation5.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation5.VerifyDiagnostics(); + CompileAndVerify(compilation5, expectedOutput: expectedOutput); + + var compilation6 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation6.VerifyDiagnostics( + // (4,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator +(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "+").WithArguments("default interface implementation", "7.1").WithLocation(4, 31), + // (10,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator -(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "-").WithArguments("default interface implementation", "7.1").WithLocation(10, 31), + // (16,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator !(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "!").WithArguments("default interface implementation", "7.1").WithLocation(16, 31), + // (22,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator ~(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "~").WithArguments("default interface implementation", "7.1").WithLocation(22, 31), + // (28,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator ++(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "++").WithArguments("default interface implementation", "7.1").WithLocation(28, 31), + // (34,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator --(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "--").WithArguments("default interface implementation", "7.1").WithLocation(34, 31), + // (40,33): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static bool operator true(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "true").WithArguments("default interface implementation", "7.1").WithLocation(40, 33), + // (46,33): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static bool operator false(I1 x) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "false").WithArguments("default interface implementation", "7.1").WithLocation(46, 33), + // (52,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator +(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "+").WithArguments("default interface implementation", "7.1").WithLocation(52, 31), + // (58,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator -(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "-").WithArguments("default interface implementation", "7.1").WithLocation(58, 31), + // (64,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator *(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "*").WithArguments("default interface implementation", "7.1").WithLocation(64, 31), + // (70,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator /(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "/").WithArguments("default interface implementation", "7.1").WithLocation(70, 31), + // (76,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator %(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "%").WithArguments("default interface implementation", "7.1").WithLocation(76, 31), + // (82,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator &(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "&").WithArguments("default interface implementation", "7.1").WithLocation(82, 31), + // (88,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator |(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "|").WithArguments("default interface implementation", "7.1").WithLocation(88, 31), + // (94,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator ^(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "^").WithArguments("default interface implementation", "7.1").WithLocation(94, 31), + // (100,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator <<(I1 x, int y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "<<").WithArguments("default interface implementation", "7.1").WithLocation(100, 31), + // (106,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator >>(I1 x, int y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, ">>").WithArguments("default interface implementation", "7.1").WithLocation(106, 31), + // (112,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator >(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, ">").WithArguments("default interface implementation", "7.1").WithLocation(112, 31), + // (118,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator <(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "<").WithArguments("default interface implementation", "7.1").WithLocation(118, 31), + // (124,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator >=(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, ">=").WithArguments("default interface implementation", "7.1").WithLocation(124, 31), + // (130,31): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // public static I1 operator <=(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "<=").WithArguments("default interface implementation", "7.1").WithLocation(130, 31) + ); + + var compilation7 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation7.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + var expected7 = new DiagnosticDescription[] + { + // (9,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = +x; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "+x").WithArguments("default interface implementation", "7.1").WithLocation(9, 13), + // (10,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = -x; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "-x").WithArguments("default interface implementation", "7.1").WithLocation(10, 13), + // (11,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = !x; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "!x").WithArguments("default interface implementation", "7.1").WithLocation(11, 13), + // (12,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = ~x; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "~x").WithArguments("default interface implementation", "7.1").WithLocation(12, 13), + // (13,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = ++x; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "++x").WithArguments("default interface implementation", "7.1").WithLocation(13, 13), + // (14,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x--; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x--").WithArguments("default interface implementation", "7.1").WithLocation(14, 13), + // (16,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x + y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x + y").WithArguments("default interface implementation", "7.1").WithLocation(16, 13), + // (17,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x - y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x - y").WithArguments("default interface implementation", "7.1").WithLocation(17, 13), + // (18,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x * y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x * y").WithArguments("default interface implementation", "7.1").WithLocation(18, 13), + // (19,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x / y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x / y").WithArguments("default interface implementation", "7.1").WithLocation(19, 13), + // (20,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x % y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x % y").WithArguments("default interface implementation", "7.1").WithLocation(20, 13), + // (21,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // if (x && y) { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x && y").WithArguments("default interface implementation", "7.1").WithLocation(21, 13), + // (21,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // if (x && y) { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x && y").WithArguments("default interface implementation", "7.1").WithLocation(21, 13), + // (22,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x | y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x | y").WithArguments("default interface implementation", "7.1").WithLocation(22, 13), + // (23,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x ^ y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x ^ y").WithArguments("default interface implementation", "7.1").WithLocation(23, 13), + // (24,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x << 1; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x << 1").WithArguments("default interface implementation", "7.1").WithLocation(24, 13), + // (25,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x >> 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x >> 2").WithArguments("default interface implementation", "7.1").WithLocation(25, 13), + // (26,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x > y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x > y").WithArguments("default interface implementation", "7.1").WithLocation(26, 13), + // (27,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x < y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x < y").WithArguments("default interface implementation", "7.1").WithLocation(27, 13), + // (28,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x >= y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x >= y").WithArguments("default interface implementation", "7.1").WithLocation(28, 13), + // (29,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x <= y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x <= y").WithArguments("default interface implementation", "7.1").WithLocation(29, 13) + }; + compilation7.VerifyDiagnostics(expected7); + + var compilation8 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation8.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation8.VerifyDiagnostics(expected7); + + var source3 = +@" +class Test3 : I1 +{ + static void Main() + { + I1 x = new Test3(); + I1 y = new Test3(); + if (x) { } + x = x & y; + } +} +"; + + var compilation9 = CreateStandardCompilation(source3, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation9.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + var expected9 = new DiagnosticDescription[] + { + // (8,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // if (x) { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x").WithArguments("default interface implementation", "7.1").WithLocation(8, 13), + // (9,13): error CS8107: Feature 'default interface implementation' is not available in C# 7. Please use language version 7.1 or greater. + // x = x & y; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "x & y").WithArguments("default interface implementation", "7.1").WithLocation(9, 13) + }; + compilation9.VerifyDiagnostics(expected9); + + var compilation10 = CreateStandardCompilation(source3, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation10.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation10.VerifyDiagnostics(expected9); + } + + [Fact] + public void Operators_02() + { + var source1 = +@" +public class C1 +{ + public static int operator +(C1 x, I1 y) + { + System.Console.WriteLine(""C1.+""); + return 0; + } + public static int operator -(I1 x, C1 y) + { + System.Console.WriteLine(""C1.-""); + return 0; + } +} + +public class C2 : C1 +{} + +class Test : I1 +{ + static void Main() + { + I1 x = new Test(); + C2 y = new C2(); + + var r = y + x; + r = x - y; + } +} +"; + + var source2 = +@" +public interface I1 +{ +} +"; + + var source3 = +@" +public interface I1 +{ + public static int operator +(C2 x, I1 y) + { + System.Console.WriteLine(""I1.+""); + return 0; + } + public static int operator -(I1 x, C2 y) + { + System.Console.WriteLine(""I1.-""); + return 0; + } +} +"; + + var expectedOutput = @" +C1.+ +C1.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + var compilation2 = CreateStandardCompilation(source1 + source3, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_03() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator ==(I1 x, I1 y) + { + System.Console.WriteLine(""==""); + return x; + } + + public static I1 operator !=(I1 x, I1 y) + { + System.Console.WriteLine(""!=""); + return x; + } +} +"; + + var source2 = +@" +class Test2 : I1 +{ + static void Main() + { + I1 x = new Test2(); + I1 y = new Test2(); + + x = x == y; + x = x != y; + } +} +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + compilation1.VerifyDiagnostics( + // (4,31): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // public static I1 operator ==(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "==").WithLocation(4, 31), + // (10,31): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // public static I1 operator !=(I1 x, I1 y) + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "!=").WithLocation(10, 31), + // (24,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + // x = x == y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x == y").WithArguments("bool", "I1").WithLocation(24, 13), + // (25,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + // x = x != y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x != y").WithArguments("bool", "I1").WithLocation(25, 13) + ); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + compilation2.VerifyDiagnostics( + // (9,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + // x = x == y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x == y").WithArguments("bool", "I1").WithLocation(9, 13), + // (10,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + // x = x != y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x != y").WithArguments("bool", "I1").WithLocation(10, 13) + ); + + var ilSource = @" +.class interface public abstract auto ansi I1 +{ + .method public hidebysig specialname static + class I1 op_Equality(class I1 x, + class I1 y) cil managed + { + // Code size 18 (0x12) + .maxstack 1 + .locals init (class I1 V_0) + IL_0000: nop + IL_0001: ldstr ""=="" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.0 + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method I1::op_Equality + + .method public hidebysig specialname static + class I1 op_Inequality(class I1 x, + class I1 y) cil managed + { + // Code size 18 (0x12) + .maxstack 1 + .locals init (class I1 V_0) + IL_0000: nop + IL_0001: ldstr ""!="" + IL_0006: call void [mscorlib]System.Console::WriteLine(string) + IL_000b: nop + IL_000c: ldarg.0 + IL_000d: stloc.0 + IL_000e: br.s IL_0010 + + IL_0010: ldloc.0 + IL_0011: ret + } // end of method I1::op_Inequality + +} // end of class I1 +"; + + var compilation3 = CreateCompilationWithCustomILSource(source2, ilSource, options: TestOptions.DebugExe); + compilation3.VerifyDiagnostics( + // (9,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + // x = x == y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x == y").WithArguments("bool", "I1").WithLocation(9, 13), + // (10,13): error CS0029: Cannot implicitly convert type 'bool' to 'I1' + // x = x != y; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x != y").WithArguments("bool", "I1").WithLocation(10, 13) + ); + + var source3 = +@" +class Test2 : I1 +{ + static void Main() + { + I1 x = new Test2(); + I1 y = new Test2(); + + System.Console.WriteLine(x == y); + System.Console.WriteLine(x != y); + } +} +"; + var compilation4 = CreateCompilationWithCustomILSource(source3, ilSource, options: TestOptions.DebugExe); + compilation4.VerifyDiagnostics(); + CompileAndVerify(compilation4, expectedOutput: @" +False +True +"); + } + + [Fact] + public void Operators_04() + { + var source1 = +@" +public interface I1 +{ + public static implicit operator int(I1 x) + { + return 0; + } + public static explicit operator byte(I1 x) + { + return 0; + } + + public static void Test(I1 x) + { + int y = x; + y = (int)x; + var z = (byte)x; + z = x; + } +} +"; + + var compilation1 = CreateStandardCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + compilation1.VerifyDiagnostics( + // (4,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // public static implicit operator int(I1 x) + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "int").WithLocation(4, 37), + // (8,37): error CS0567: Interfaces cannot contain conversion, equality, or inequality operators + // public static explicit operator byte(I1 x) + Diagnostic(ErrorCode.ERR_InterfacesCantContainConversionOrEqualityOperators, "byte").WithLocation(8, 37), + // (15,17): error CS0029: Cannot implicitly convert type 'I1' to 'int' + // int y = x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("I1", "int").WithLocation(15, 17), + // (16,13): error CS0030: Cannot convert type 'I1' to 'int' + // y = (int)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(int)x").WithArguments("I1", "int").WithLocation(16, 13), + // (17,17): error CS0030: Cannot convert type 'I1' to 'byte' + // var z = (byte)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(byte)x").WithArguments("I1", "byte").WithLocation(17, 17), + // (18,13): error CS0029: Cannot implicitly convert type 'I1' to 'byte' + // z = x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("I1", "byte").WithLocation(18, 13) + ); + } + + [Fact] + public void Operators_05() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x) + { + System.Console.WriteLine(""-""); + return x; + } +} + +public interface I2 : I1 +{} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + + I1 y = -x; + } +} +"; + + var expectedOutput = +@" +- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + + var source3 = +@" +class Test2 : I2 +{ + static void Main() + { + Test2 x = new Test2(); + + I1 y = -x; + } +} +"; + + var compilation9 = CreateStandardCompilation(source3, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation9.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + var expected9 = new DiagnosticDescription[] + { + // (8,16): error CS0023: Operator '-' cannot be applied to operand of type 'Test2' + // I1 y = -x; + Diagnostic(ErrorCode.ERR_BadUnaryOp, "-x").WithArguments("-", "Test2").WithLocation(8, 16) + }; + compilation9.VerifyDiagnostics(expected9); + + var compilation10 = CreateStandardCompilation(source3, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation10.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation10.VerifyDiagnostics(expected9); + } + + [Fact] + public void Operators_06() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I2 : I1 +{ + public static I2 operator -(I2 x) + { + System.Console.WriteLine(""I2.-""); + return x; + } +} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + var y = -x; + } +} +"; + + var expectedOutput = +@" +I2.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_07() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I3 +{ + public static I3 operator -(I3 x) + { + System.Console.WriteLine(""I3.-""); + return x; + } +} + +public interface I4 : I1, I3 +{ + public static I4 operator -(I4 x) + { + System.Console.WriteLine(""I4.-""); + return x; + } +} + +public interface I2 : I4 +{ +} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + var y = -x; + } +} +"; + + var expectedOutput = +@" +I4.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_08() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I3 +{ + public static I3 operator -(I3 x) + { + System.Console.WriteLine(""I3.-""); + return x; + } +} + +public interface I4 : I1, I3 +{ +} + +public interface I2 : I4 +{ +} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + var y = -x; + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (7,17): error CS0035: Operator '-' is ambiguous on an operand of type 'I2' + // var y = -x; + Diagnostic(ErrorCode.ERR_AmbigUnaryOp, "-x").WithArguments("-", "I2").WithLocation(7, 17) + }; + + var compilation1 = CreateStandardCompilation(source2 + source1, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(expected); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + } + + [Fact] + public void Operators_09() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} +"; + + var source2 = +@" +class Test1 : Test2, I1 {} + +class Test2 +{ + static void Main() + { + Test(new Test1()); + } + + static void Test(T x) where T : Test2, I1 + { + var y = -x; + } + + public static Test2 operator -(Test2 x) + { + System.Console.WriteLine(""Test2.-""); + return x; + } +} +"; + + var expectedOutput = +@" +Test2.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_10() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I3 +{ + public static I3 operator -(I3 x) + { + System.Console.WriteLine(""I3.-""); + return x; + } +} + +public interface I4 : I1, I3 +{ + public static I4 operator -(I4 x) + { + System.Console.WriteLine(""I4.-""); + return x; + } +} + +public interface I2 : I4 +{ +}"; + + var source2 = +@" +class Test1 : Test2, I2 {} + +class Test2 +{ + static void Main() + { + Test(new Test1()); + } + + static void Test(T x) where T : Test2, I2 + { + var y = -x; + } +} +"; + + var expectedOutput = +@" +I4.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_11() + { + var source0 = +@" +public interface I1 +{ } + +public interface I2 +{ } +"; + var source1 = +@" +public class C1 +{ + public static C1 operator +(C1 x, I2 y) + { + System.Console.WriteLine(""C1.+1""); + return x; + } + public static C1 operator +(C1 x, I1 y) + { + System.Console.WriteLine(""C1.+2""); + return x; + } +} + +"; + + var source2 = +@" +public interface I3 : I1, I2 +{ } +"; + var source3 = +@" +class Test2 : I3 +{ + static void Main() + { + I3 x = new Test2(); + var y = new C1() + x; + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (7,17): error CS0034: Operator '+' is ambiguous on operands of type 'C1' and 'I3' + // var y = new C1() + x; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "new C1() + x").WithArguments("+", "C1", "I3").WithLocation(7, 17) + }; + + var compilation0 = CreateStandardCompilation(source0 + source1 + source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation0.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation0.VerifyDiagnostics(); + + CompilationReference compilationReference0 = compilation0.ToMetadataReference(); + MetadataReference metadataReference0 = compilation0.EmitToImageReference(); + + var compilation1 = CreateStandardCompilation(source3, new[] { compilationReference0 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(expected); + + var compilation2 = CreateStandardCompilation(source3, new[] { metadataReference0 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + + var source4 = +@" +public interface I1 +{ + public static C1 operator +(C1 x, I1 y) + { + System.Console.WriteLine(""I1.+""); + return x; + } +} + +public interface I2 +{ } +"; + var compilation3 = CreateStandardCompilation(source4 + source1 + source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + + CompilationReference compilationReference3 = compilation3.ToMetadataReference(); + MetadataReference metadataReference3 = compilation3.EmitToImageReference(); + + var compilation4 = CreateStandardCompilation(source3, new[] { compilationReference3 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation4.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation4.VerifyDiagnostics(expected); + + var compilation5 = CreateStandardCompilation(source3, new[] { metadataReference3 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation5.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation5.VerifyDiagnostics(expected); + + var source5 = + @" +public interface I3 : I1, I2 +{ + public static C1 operator +(C1 x, I3 y) + { + System.Console.WriteLine(""I3.+""); + return x; + } +} +"; + var compilation6 = CreateStandardCompilation(source4 + source1 + source5, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation6.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation6.VerifyDiagnostics(); + + CompilationReference compilationReference6 = compilation6.ToMetadataReference(); + MetadataReference metadataReference6 = compilation6.EmitToImageReference(); + + var compilation7 = CreateStandardCompilation(source3, new[] { compilationReference6 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation7.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + CompileAndVerify(compilation7, expectedOutput: "I3.+"); + + var compilation8 = CreateStandardCompilation(source3, new[] { metadataReference6 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation8.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + CompileAndVerify(compilation8, expectedOutput: "I3.+"); + } + + [Fact] + public void Operators_12() + { + var source0 = +@" +public interface I1 +{ } + +public interface I2 +{ } +"; + var source1 = +@" +public class C1 +{ + public static C1 operator +(I2 x, C1 y) + { + System.Console.WriteLine(""C1.+1""); + return y; + } + public static C1 operator +(I1 x, C1 y) + { + System.Console.WriteLine(""C1.+2""); + return y; + } +} + +"; + + var source2 = +@" +public interface I3 : I1, I2 +{ } +"; + var source3 = +@" +class Test2 : I3 +{ + static void Main() + { + I3 x = new Test2(); + var y = x + new C1(); + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (7,17): error CS0034: Operator '+' is ambiguous on operands of type 'I3' and 'C1' + // var y = x + new C1(); + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + new C1()").WithArguments("+", "I3", "C1").WithLocation(7, 17) + }; + + var compilation0 = CreateStandardCompilation(source0 + source1 + source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation0.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation0.VerifyDiagnostics(); + + CompilationReference compilationReference0 = compilation0.ToMetadataReference(); + MetadataReference metadataReference0 = compilation0.EmitToImageReference(); + + var compilation1 = CreateStandardCompilation(source3, new[] { compilationReference0 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(expected); + + var compilation2 = CreateStandardCompilation(source3, new[] { metadataReference0 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + + var source4 = +@" +public interface I1 +{ + public static C1 operator +(I1 x, C1 y) + { + System.Console.WriteLine(""I1.+""); + return y; + } +} + +public interface I2 +{ } +"; + var compilation3 = CreateStandardCompilation(source4 + source1 + source2, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + + CompilationReference compilationReference3 = compilation3.ToMetadataReference(); + MetadataReference metadataReference3 = compilation3.EmitToImageReference(); + + var compilation4 = CreateStandardCompilation(source3, new[] { compilationReference3 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation4.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation4.VerifyDiagnostics(expected); + + var compilation5 = CreateStandardCompilation(source3, new[] { metadataReference3 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation5.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation5.VerifyDiagnostics(expected); + + var source5 = + @" +public interface I3 : I1, I2 +{ + public static C1 operator +(I3 x, C1 y) + { + System.Console.WriteLine(""I3.+""); + return y; + } +} +"; + var compilation6 = CreateStandardCompilation(source4 + source1 + source5, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation6.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation6.VerifyDiagnostics(); + + CompilationReference compilationReference6 = compilation6.ToMetadataReference(); + MetadataReference metadataReference6 = compilation6.EmitToImageReference(); + + var compilation7 = CreateStandardCompilation(source3, new[] { compilationReference6 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation7.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + CompileAndVerify(compilation7, expectedOutput: "I3.+"); + + var compilation8 = CreateStandardCompilation(source3, new[] { metadataReference6 }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation8.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + CompileAndVerify(compilation8, expectedOutput: "I3.+"); + } + + [Fact] + public void Operators_13() + { + var source1 = +@" +public interface I1 +{ + public static int operator +(I1 x, I2 y) + { + System.Console.WriteLine(""I1.+""); + return 1; + } +} + +public interface I2 +{ + public static int operator +(I1 x, I2 y) + { + System.Console.WriteLine(""I2.+""); + return 2; + } +} +"; + + var source2 = +@" +class Test2: I1, I2 +{ + static void Main() + { + I1 x = new Test2(); + I2 y = new Test2(); + var z = x + y; + z = y + x; + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I1' and 'I2' + // var z = x + y; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + y").WithArguments("+", "I1", "I2").WithLocation(8, 17), + // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I2' and 'I1' + // z = y + x; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "y + x").WithArguments("+", "I2", "I1").WithLocation(9, 13) + }; + + var compilation1 = CreateStandardCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(expected); + } + + [Fact] + public void Operators_14() + { + var source1 = +@" +public interface I1 +{ + public static int operator +(I2 x, I1 y) + { + System.Console.WriteLine(""I1.+""); + return 1; + } +} + +public interface I2 : I1 +{ + public static int operator +(I2 x, I1 y) + { + System.Console.WriteLine(""I2.+""); + return 2; + } +} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I1 x = new Test2(); + I2 y = new Test2(); + var z = y + x; + z = x + y; + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I2' and 'I1' + // var z = y + x; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "y + x").WithArguments("+", "I2", "I1").WithLocation(8, 17), + // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I1' and 'I2' + // z = x + y; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "x + y").WithArguments("+", "I1", "I2").WithLocation(9, 13) + }; + + var compilation1 = CreateStandardCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(expected); + } + + [Fact] + public void Operators_15() + { + var source1 = +@" +public class I1 +{ + public static int operator +(I1 x, C2 y) + { + System.Console.WriteLine(""I1.+1""); + return 1; + } + public static int operator +(C2 x, I1 y) + { + System.Console.WriteLine(""I1.+2""); + return 1; + } +} + +public class I2 : I1 +{ + public static int operator +(I2 x, C1 y) + { + System.Console.WriteLine(""I2.+1""); + return 1; + } + public static int operator +(C1 x, I2 y) + { + System.Console.WriteLine(""I2.+2""); + return 1; + } +} + +public class C1 { } +public class C2 : C1 { } +"; + + var source2 = +@" +class Test2: I2 +{ + static void Main() + { + I2 x = new Test2(); + C2 y = new C2(); + var z = x + y; + z = y + x; } +} +"; + + var expectedOutput = +@" +I2.+1 +I2.+2 +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_16() + { + var source1 = +@" +public interface I1 +{ + public static int operator +(I1 x, I1 y) + { + System.Console.WriteLine(""I1.+""); + return 1; + } +} +"; + + var source2 = +@" +class Test2: I1 +{ + static void Main() + { + I1 x = new Test2(); + I1 y = new Test2(); + var z = x + y; + z = y + x; + } +} +"; + + var expectedOutput = +@" +I1.+ +I1.+ +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_17() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator +(I1 x, C1 y) + { + System.Console.WriteLine(""I1.+1""); + return x; + } + public static I1 operator +(C1 x, I1 y) + { + System.Console.WriteLine(""I1.+2""); + return y; + } +} + +public interface I2 : I1 +{} + +public class C1{} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + C1 y = new C1(); + var z = x + y; + z = y + x; + } +} +"; + + var expectedOutput = +@" +I1.+1 +I1.+2 +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + + var source3 = +@" +class Test2 : I2 +{ + static void Main() + { + Test2 x = new Test2(); + C1 y = new C1(); + var z = x + y; + z = y + x; + } +} +"; + + var compilation9 = CreateStandardCompilation(source3, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation9.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + var expected9 = new DiagnosticDescription[] + { + // (8,17): error CS0019: Operator '+' cannot be applied to operands of type 'Test2' and 'C1' + // var z = x + y; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "x + y").WithArguments("+", "Test2", "C1").WithLocation(8, 17), + // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'C1' and 'Test2' + // z = y + x; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "y + x").WithArguments("+", "C1", "Test2").WithLocation(9, 13) + }; + compilation9.VerifyDiagnostics(expected9); + + var compilation10 = CreateStandardCompilation(source3, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); + Assert.True(compilation10.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation10.VerifyDiagnostics(expected9); + } + + [Fact] + public void Operators_18() + { + var source1 = +@" +public interface I1 +{ + public static int operator +(I1 x, I1 y) + { + System.Console.WriteLine(""I1.+""); + return 1; + } +} + +public interface I2 : I1 +{} +"; + + var source2 = +@" +class Test2: I2 +{ + static void Main() + { + I2 x = new Test2(); + I1 y = new Test2(); + var z = x + y; + z = y + x; + z = x + x; + z = y + y; + } +} +"; + + var expectedOutput = +@" +I1.+ +I1.+ +I1.+ +I1.+ +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_19() + { + var source1 = +@" +public interface I1 +{ + public static int operator +(I1 x, I1 y) + { + System.Console.WriteLine(""I1.+""); + return 1; + } +} + +public interface I2 : I1 +{} +"; + + var source2 = +@" +class Test2: I2 +{ + static void Main() + { + I2 x = new Test2(); + I2 y = new Test2(); + var z = x + y; + z = y + x; + I1 u = x; + I1 v = y; + z = y + u; + z = u + y; + z = x + v; + z = v + x; + } +} +"; + + var expectedOutput = +@" +I1.+ +I1.+ +I1.+ +I1.+ +I1.+ +I1.+ +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_20() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x, I2 y) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I2 : I1 +{ + public static I2 operator -(I1 x, I2 y) + { + System.Console.WriteLine(""I2.-""); + return y; + } +} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + I2 y = new Test2(); + var z = x - y; + } +} +"; + + var expectedOutput = +@" +I2.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_21() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x, I2 y) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I2 : I1 +{ + public static I2 operator -(I1 x, I2 y) + { + System.Console.WriteLine(""I2.-""); + return y; + } +} + +public interface I3 : I2 +{} +"; + + var source2 = +@" +class Test2 : I3 +{ + static void Main() + { + I3 x = new Test2(); + I3 y = new Test2(); + var z = x - y; + } +} +"; + + var expectedOutput = +@" +I2.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_22() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x, I2 y) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I2 : I1 +{ + public static I2 operator -(I1 x, I2 y) + { + System.Console.WriteLine(""I2.-""); + return y; + } +} + +public interface I3 : I2 +{} + +public interface I4 : I3 +{} +"; + + var source2 = +@" +class Test2 : I3, I4 +{ + static void Main() + { + I3 x = new Test2(); + I4 y = new Test2(); + var z = x - y; + z = y - x; + } +} +"; + + var expectedOutput = +@" +I2.- +I2.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_23() + { + var source1 = +@" +public interface I1 +{ + public static int operator +(I1 x, I2 y) + { + System.Console.WriteLine(""I1.+""); + return 1; + } +} + +public interface I2 : I1 +{ + public static int operator +(I1 x, I2 y) + { + System.Console.WriteLine(""I2.+""); + return 2; + } +} + +public interface I3 : I2 +{} +"; + + var source2 = +@" +class Test2 : I3 +{ + static void Main() + { + I1 x = new Test2(); + I3 y = new Test2(); + var z = x + y; + z = y + x; + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I1' and 'I3' + // var z = x + y; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x + y").WithArguments("+", "I1", "I3").WithLocation(8, 17), + // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I3' and 'I1' + // z = y + x; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "y + x").WithArguments("+", "I3", "I1").WithLocation(9, 13) + }; + + var compilation1 = CreateStandardCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(expected); + } + + [Fact] + public void Operators_24() + { + var source1 = +@" +public interface I1 +{ + public static int operator +(I2 x, I1 y) + { + System.Console.WriteLine(""I1.+""); + return 1; + } +} + +public interface I2 : I1 +{ + public static int operator +(I2 x, I1 y) + { + System.Console.WriteLine(""I2.+""); + return 2; + } +} + +public interface I3 : I2 +{} +"; + + var source2 = +@" +class Test2 : I3 +{ + static void Main() + { + I1 x = new Test2(); + I3 y = new Test2(); + var z = y + x; + z = x + y; + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (8,17): error CS0034: Operator '+' is ambiguous on operands of type 'I3' and 'I1' + // var z = y + x; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "y + x").WithArguments("+", "I3", "I1").WithLocation(8, 17), + // (9,13): error CS0019: Operator '+' cannot be applied to operands of type 'I1' and 'I3' + // z = x + y; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "x + y").WithArguments("+", "I1", "I3").WithLocation(9, 13) + }; + + var compilation1 = CreateStandardCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(expected); + } + + [Fact] + public void Operators_25() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x, I1 y) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} + +public interface I3 +{ + public static I3 operator -(I3 x, I3 y) + { + System.Console.WriteLine(""I3.-""); + return x; + } +} + +public interface I4 : I1, I3 +{ +} + +public interface I2 : I4 +{ +} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + var y = x - x; + } +} +"; + + var expected = new DiagnosticDescription[] + { + // (7,17): error CS0034: Operator '-' is ambiguous on operands of type 'I2' and 'I2' + // var y = x - x; + Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "x - x").WithArguments("-", "I2", "I2").WithLocation(7, 17) + }; + + var compilation1 = CreateStandardCompilation(source2 + source1, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(expected); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(expected); + } + + [Fact] + public void Operators_26() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x, C1 y) + { + System.Console.WriteLine(""I1.-1""); + return x; + } + public static I1 operator -(C1 x, I1 y) + { + System.Console.WriteLine(""I1.-2""); + return y; + } +} + +public interface I3 +{ + public static I3 operator -(I3 x, C2 y) + { + System.Console.WriteLine(""I3.-1""); + return x; + } + public static I3 operator -(C2 x, I3 y) + { + System.Console.WriteLine(""I3.-2""); + return y; + } +} + +public interface I4 +{ + public static I4 operator -(I4 x, C1 y) + { + System.Console.WriteLine(""I4.-1""); + return x; + } + public static I4 operator -(C1 x, I4 y) + { + System.Console.WriteLine(""I4.-2""); + return y; + } +} + +public interface I5 : I1, I3, I4 +{ +} + +public interface I2 : I5 +{ +} + +public class C1{} +public class C2 : C1{} +"; + + var source2 = +@" +class Test2 : I2 +{ + static void Main() + { + I2 x = new Test2(); + var c = new C2(); + var y = x - c; + y = c - x; + } +} +"; + + var expectedOutput = +@" +I3.-1 +I3.-2 +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_27() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x, I1 y) + { + System.Console.WriteLine(""I1.-""); + return x; + } +} +"; + + var source2 = +@" +class Test1 : Test2, I1 {} + +class Test2 +{ + static void Main() + { + Test(new Test1()); + } + + static void Test(T x) where T : Test2, I1 + { + var y = x - x; + } + + public static Test2 operator -(Test2 x, Test2 y) + { + System.Console.WriteLine(""Test2.-""); + return x; + } +} +"; + + var expectedOutput = +@" +Test2.- +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_28() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator -(I1 x, C1 y) + { + System.Console.WriteLine(""I1.-1""); + return x; + } + public static I1 operator -(C1 x, I1 y) + { + System.Console.WriteLine(""I1.-2""); + return y; + } +} + +public interface I3 +{ + public static I3 operator -(I3 x, C2 y) + { + System.Console.WriteLine(""I3.-1""); + return x; + } + public static I3 operator -(C2 x, I3 y) + { + System.Console.WriteLine(""I3.-2""); + return y; + } +} + +public interface I4 +{ + public static I4 operator -(I4 x, C1 y) + { + System.Console.WriteLine(""I4.-1""); + return x; + } + public static I4 operator -(C1 x, I4 y) + { + System.Console.WriteLine(""I4.-2""); + return y; + } +} + +public interface I5 : I1, I3, I4 +{ +} + +public interface I2 : I5 +{ +} + +public class C1{} +public class C2 : C1{} +"; + + var source2 = +@" +class Test1 : Test2, I2 {} + +class Test2 +{ + static void Main() + { + Test(new Test1()); + } + + static void Test(T x) where T : Test2, I2 + { + var c = new C2(); + var y = c - x; + y = x - c; + } +} +"; + + var expectedOutput = +@" +I3.-2 +I3.-1 +"; + + var compilation1 = CreateStandardCompilation(source1 + source2, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics(); + CompileAndVerify(compilation1, expectedOutput: expectedOutput); + + CompilationReference compilationReference = compilation1.ToMetadataReference(); + MetadataReference metadataReference = compilation1.EmitToImageReference(); + + var compilation2 = CreateStandardCompilation(source2, new[] { compilationReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation2.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation2.VerifyDiagnostics(); + CompileAndVerify(compilation2, expectedOutput: expectedOutput); + + var compilation3 = CreateStandardCompilation(source2, new[] { metadataReference }, options: TestOptions.DebugExe, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation3.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation3.VerifyDiagnostics(); + CompileAndVerify(compilation3, expectedOutput: expectedOutput); + } + + [Fact] + public void Operators_29() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator +(int x) => throw null; + public static I1 operator -(int x) => throw null; + public static I1 operator !(int x) => throw null; + public static I1 operator ~(int x) => throw null; + public static I1 operator ++(int x) => throw null; + public static I1 operator --(int x) => throw null; + public static bool operator true(int x) => throw null; + public static bool operator false(int x) => throw null; + public static I1 operator +(int x, int y) => throw null; + public static I1 operator -(int x, int y) => throw null; + public static I1 operator *(int x, int y) => throw null; + public static I1 operator /(int x, int y) => throw null; + public static I1 operator %(int x, int y) => throw null; + public static I1 operator &(int x, int y) => throw null; + public static I1 operator |(int x, int y) => throw null; + public static I1 operator ^(int x, int y) => throw null; + public static I1 operator <<(int x, int y) => throw null; + public static I1 operator >>(int x, int y) => throw null; + public static I1 operator >(int x, int y) => throw null; + public static I1 operator <(int x, int y) => throw null; + public static I1 operator >=(int x, int y) => throw null; + public static I1 operator <=(int x, int y) => throw null; +} +"; + + var compilation1 = CreateStandardCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics( + // (4,31): error CS0562: The parameter of a unary operator must be the containing type + // public static I1 operator +(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, "+").WithLocation(4, 31), + // (5,31): error CS0562: The parameter of a unary operator must be the containing type + // public static I1 operator -(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, "-").WithLocation(5, 31), + // (6,31): error CS0562: The parameter of a unary operator must be the containing type + // public static I1 operator !(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, "!").WithLocation(6, 31), + // (7,31): error CS0562: The parameter of a unary operator must be the containing type + // public static I1 operator ~(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, "~").WithLocation(7, 31), + // (8,31): error CS0559: The parameter type for ++ or -- operator must be the containing type + // public static I1 operator ++(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadIncDecSignature, "++").WithLocation(8, 31), + // (9,31): error CS0559: The parameter type for ++ or -- operator must be the containing type + // public static I1 operator --(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadIncDecSignature, "--").WithLocation(9, 31), + // (10,33): error CS0562: The parameter of a unary operator must be the containing type + // public static bool operator true(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, "true").WithLocation(10, 33), + // (11,33): error CS0562: The parameter of a unary operator must be the containing type + // public static bool operator false(int x) => throw null; + Diagnostic(ErrorCode.ERR_BadUnaryOperatorSignature, "false").WithLocation(11, 33), + // (12,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator +(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "+").WithLocation(12, 31), + // (13,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator -(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "-").WithLocation(13, 31), + // (14,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator *(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "*").WithLocation(14, 31), + // (15,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator /(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "/").WithLocation(15, 31), + // (16,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator %(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "%").WithLocation(16, 31), + // (17,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator &(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "&").WithLocation(17, 31), + // (18,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator |(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "|").WithLocation(18, 31), + // (19,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator ^(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "^").WithLocation(19, 31), + // (20,31): error CS0564: 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 + // public static I1 operator <<(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadShiftOperatorSignature, "<<").WithLocation(20, 31), + // (21,31): error CS0564: 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 + // public static I1 operator >>(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadShiftOperatorSignature, ">>").WithLocation(21, 31), + // (22,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator >(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, ">").WithLocation(22, 31), + // (23,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator <(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "<").WithLocation(23, 31), + // (24,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator >=(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, ">=").WithLocation(24, 31), + // (25,31): error CS0563: One of the parameters of a binary operator must be the containing type + // public static I1 operator <=(int x, int y) => throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "<=").WithLocation(25, 31) + ); + } + + [Fact] + public void Operators_30() + { + var source1 = +@" +public interface I1 +{ + public static I1 operator <<(I1 x, I1 y) => throw null; + public static I1 operator >>(I1 x, I1 y) => throw null; +} + +public interface I2 +{ + public static bool operator true(I2 x) => throw null; +} + +public interface I3 +{ + public static bool operator false(I3 x) => throw null; +} + +public interface I4 +{ + public static int operator true(I4 x) => throw null; + public static int operator false(I4 x) => throw null; +} +"; + + var compilation1 = CreateStandardCompilation(source1, options: TestOptions.DebugDll, + parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.Latest)); + Assert.True(compilation1.Assembly.RuntimeSupportsDefaultInterfaceImplementation); + compilation1.VerifyDiagnostics( + // (4,31): error CS0564: 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 + // public static I1 operator <<(I1 x, I1 y) => throw null; + Diagnostic(ErrorCode.ERR_BadShiftOperatorSignature, "<<").WithLocation(4, 31), + // (5,31): error CS0564: 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 + // public static I1 operator >>(I1 x, I1 y) => throw null; + Diagnostic(ErrorCode.ERR_BadShiftOperatorSignature, ">>").WithLocation(5, 31), + // (10,33): error CS0216: The operator 'I2.operator true(I2)' requires a matching operator 'false' to also be defined + // public static bool operator true(I2 x) => throw null; + Diagnostic(ErrorCode.ERR_OperatorNeedsMatch, "true").WithArguments("I2.operator true(I2)", "false").WithLocation(10, 33), + // (15,33): error CS0216: The operator 'I3.operator false(I3)' requires a matching operator 'true' to also be defined + // public static bool operator false(I3 x) => throw null; + Diagnostic(ErrorCode.ERR_OperatorNeedsMatch, "false").WithArguments("I3.operator false(I3)", "true").WithLocation(15, 33), + // (20,32): error CS0215: The return type of operator True or False must be bool + // public static int operator true(I4 x) => throw null; + Diagnostic(ErrorCode.ERR_OpTFRetType, "true").WithLocation(20, 32), + // (21,32): error CS0215: The return type of operator True or False must be bool + // public static int operator false(I4 x) => throw null; + Diagnostic(ErrorCode.ERR_OpTFRetType, "false").WithLocation(21, 32) + ); + } + } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index 7e8ed203eea04..ce1f5497c9969 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -10399,11 +10399,17 @@ interface IA } "; - var comp = CreateStandardCompilation(text); + var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7); comp.VerifyDiagnostics( -// (4,17): error CS0567: Interfaces cannot contain operators -// int operator +(int aa, int bb); // CS0567 -Diagnostic(ErrorCode.ERR_InterfacesCantContainOperators, "+") + // (4,17): error CS0558: User-defined operator 'IA.operator +(int, int)' must be declared static and public + // int operator +(int aa, int bb); // CS0567 + Diagnostic(ErrorCode.ERR_OperatorsMustBeStatic, "+").WithArguments("IA.operator +(int, int)").WithLocation(4, 17), + // (4,17): error CS0501: 'IA.operator +(int, int)' must declare a body because it is not marked abstract, extern, or partial + // int operator +(int aa, int bb); // CS0567 + 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) ); } diff --git a/src/Compilers/Core/Portable/InternalUtilities/ISetExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/ISetExtensions.cs index ba48dfd5e3c29..197ed59a81b7b 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ISetExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/ISetExtensions.cs @@ -1,6 +1,7 @@ // 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.Collections.Generic; +using System.Collections.Immutable; namespace Roslyn.Utilities { @@ -17,6 +18,17 @@ public static bool AddAll(this ISet set, IEnumerable values) return result; } + public static bool AddAll(this ISet set, ImmutableArray values) + { + var result = false; + foreach (var v in values) + { + result |= set.Add(v); + } + + return result; + } + public static bool RemoveAll(this ISet set, IEnumerable values) { var result = false; @@ -27,5 +39,16 @@ public static bool RemoveAll(this ISet set, IEnumerable values) return result; } + + public static bool RemoveAll(this ISet set, ImmutableArray values) + { + var result = false; + foreach (var v in values) + { + result |= set.Remove(v); + } + + return result; + } } }