@@ -1092,33 +1092,49 @@ private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, BindingD
10921092 out LookupResultKind resultKind , out ImmutableArray < MethodSymbol > originalUserDefinedOperators ,
10931093 out BinaryOperatorSignature resultSignature , out BinaryOperatorAnalysisResult best )
10941094 {
1095- bool foundOperator ;
1096-
10971095 if ( ! IsTypelessExpressionAllowedInBinaryOperator ( kind , left , right ) )
10981096 {
10991097 resultKind = LookupResultKind . OverloadResolutionFailure ;
11001098 originalUserDefinedOperators = default ( ImmutableArray < MethodSymbol > ) ;
11011099 best = default ( BinaryOperatorAnalysisResult ) ;
1100+ resultSignature = new BinaryOperatorSignature ( kind , leftType : null , rightType : null , CreateErrorType ( ) ) ;
1101+ return false ;
11021102 }
1103- else
1104- {
1105- best = this . BinaryOperatorOverloadResolution ( kind , isChecked : CheckOverflowAtRuntime , left , right , node , diagnostics , out resultKind , out originalUserDefinedOperators ) ;
1106- }
11071103
1108- // However, as an implementation detail, we never "fail to find an applicable
1109- // operator" during overload resolution if we have x == null, x == default, etc. We always
1110- // find at least the reference conversion object == object; the overload resolution
1111- // code does not reject that. Therefore what we should do is only bind
1112- // "x == null" as a nullable-to-null comparison if overload resolution chooses
1113- // the reference conversion.
1104+ bool isChecked = CheckOverflowAtRuntime ;
1105+ OverloadResolution . GetStaticUserDefinedBinaryOperatorMethodNames ( kind , isChecked , out string name1 , out string name2Opt ) ;
1106+ best = this . BinaryOperatorOverloadResolution ( kind , isChecked , name1 , name2Opt , left , right , node , diagnostics , out resultKind , out originalUserDefinedOperators ) ;
1107+
1108+ return bindSimpleBinaryOperatorPartsContinue ( node , diagnostics , left , right , kind , ref resultKind , ref originalUserDefinedOperators , out resultSignature , ref best , isChecked , name1 , name2Opt ) ;
11141109
1115- if ( ! best . HasValue )
1116- {
1117- resultSignature = new BinaryOperatorSignature ( kind , leftType : null , rightType : null , CreateErrorType ( ) ) ;
1118- foundOperator = false ;
1119- }
1120- else
1110+ bool bindSimpleBinaryOperatorPartsContinue (
1111+ BinaryExpressionSyntax node ,
1112+ BindingDiagnosticBag diagnostics ,
1113+ BoundExpression left ,
1114+ BoundExpression right ,
1115+ BinaryOperatorKind kind ,
1116+ ref LookupResultKind resultKind ,
1117+ ref ImmutableArray < MethodSymbol > originalUserDefinedOperators ,
1118+ out BinaryOperatorSignature resultSignature ,
1119+ ref BinaryOperatorAnalysisResult best ,
1120+ bool isChecked ,
1121+ string name1 ,
1122+ string name2Opt )
11211123 {
1124+ // However, as an implementation detail, we never "fail to find an applicable
1125+ // operator" during overload resolution if we have x == null, x == default, etc. We always
1126+ // find at least the reference conversion object == object; the overload resolution
1127+ // code does not reject that. Therefore what we should do is only bind
1128+ // "x == null" as a nullable-to-null comparison if overload resolution chooses
1129+ // the reference conversion.
1130+
1131+ if ( ! best . HasValue )
1132+ {
1133+ resultSignature = new BinaryOperatorSignature ( kind , leftType : null , rightType : null , CreateErrorType ( ) ) ;
1134+ return false ;
1135+ }
1136+
1137+ bool foundOperator ;
11221138 var signature = best . Signature ;
11231139
11241140 if ( signature . Method is { } bestMethod )
@@ -1155,9 +1171,27 @@ private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, BindingD
11551171 bool rightDefault = right . IsLiteralDefault ( ) ;
11561172 foundOperator = ! isObjectEquality || BuiltInOperators . IsValidObjectEquality ( Conversions , leftType , leftNull , leftDefault , rightType , rightNull , rightDefault , ref useSiteInfo ) ;
11571173 diagnostics . Add ( node , useSiteInfo ) ;
1174+
1175+ if ( ! foundOperator )
1176+ {
1177+ Debug . Assert ( isObjectEquality ) ;
1178+
1179+ // Try extension operators since predefined object equality was not applicable
1180+ LookupResultKind extensionResultKind ;
1181+ ImmutableArray < MethodSymbol > extensionOriginalUserDefinedOperators ;
1182+ BinaryOperatorAnalysisResult ? extensionBest = BinaryOperatorExtensionOverloadResolution ( kind , isChecked , name1 , name2Opt , left , right , node , diagnostics , out extensionResultKind , out extensionOriginalUserDefinedOperators ) ;
1183+
1184+ if ( extensionBest . HasValue )
1185+ {
1186+ best = extensionBest . GetValueOrDefault ( ) ;
1187+ resultKind = extensionResultKind ;
1188+ originalUserDefinedOperators = extensionOriginalUserDefinedOperators ;
1189+ foundOperator = bindSimpleBinaryOperatorPartsContinue ( node , diagnostics , left , right , kind , ref resultKind , ref originalUserDefinedOperators , out resultSignature , ref best , isChecked , name1 , name2Opt ) ;
1190+ }
1191+ }
11581192 }
1193+ return foundOperator ;
11591194 }
1160- return foundOperator ;
11611195 }
11621196
11631197#nullable enable
@@ -1914,13 +1948,28 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution(
19141948 {
19151949 OverloadResolution . GetStaticUserDefinedBinaryOperatorMethodNames ( kind , isChecked , out string name1 , out string name2Opt ) ;
19161950
1951+ return BinaryOperatorOverloadResolution ( kind , isChecked , name1 , name2Opt , left , right , node , diagnostics , out resultKind , out originalUserDefinedOperators ) ;
1952+ }
1953+
1954+ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution (
1955+ BinaryOperatorKind kind ,
1956+ bool isChecked ,
1957+ string name1 ,
1958+ string name2Opt ,
1959+ BoundExpression left ,
1960+ BoundExpression right ,
1961+ CSharpSyntaxNode node ,
1962+ BindingDiagnosticBag diagnostics ,
1963+ out LookupResultKind resultKind ,
1964+ out ImmutableArray < MethodSymbol > originalUserDefinedOperators )
1965+ {
19171966 BinaryOperatorAnalysisResult possiblyBest = BinaryOperatorNonExtensionOverloadResolution ( kind , isChecked , name1 , name2Opt , left , right , node , diagnostics , out resultKind , out originalUserDefinedOperators ) ;
19181967
19191968 if ( ! possiblyBest . HasValue && resultKind != LookupResultKind . Ambiguous )
19201969 {
19211970 LookupResultKind extensionResultKind ;
19221971 ImmutableArray < MethodSymbol > extensionOriginalUserDefinedOperators ;
1923- BinaryOperatorAnalysisResult ? extensionBest = extensionOverloadResolution ( kind , isChecked , name1 , name2Opt , left , right , node , diagnostics , out extensionResultKind , out extensionOriginalUserDefinedOperators ) ;
1972+ BinaryOperatorAnalysisResult ? extensionBest = BinaryOperatorExtensionOverloadResolution ( kind , isChecked , name1 , name2Opt , left , right , node , diagnostics , out extensionResultKind , out extensionOriginalUserDefinedOperators ) ;
19241973
19251974 if ( extensionBest . HasValue && ( extensionBest . GetValueOrDefault ( ) . HasValue || ( originalUserDefinedOperators . IsEmpty && ! extensionOriginalUserDefinedOperators . IsEmpty ) ) )
19261975 {
@@ -1931,55 +1980,55 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution(
19311980 }
19321981
19331982 return possiblyBest ;
1983+ }
19341984
1935- #nullable enable
1985+ #nullable enable
19361986
1937- BinaryOperatorAnalysisResult ? extensionOverloadResolution (
1938- BinaryOperatorKind kind ,
1939- bool isChecked ,
1940- string name1 ,
1941- string name2Opt ,
1942- BoundExpression left ,
1943- BoundExpression right ,
1944- CSharpSyntaxNode node ,
1945- BindingDiagnosticBag diagnostics ,
1946- out LookupResultKind resultKind ,
1947- out ImmutableArray < MethodSymbol > originalUserDefinedOperators )
1987+ private BinaryOperatorAnalysisResult ? BinaryOperatorExtensionOverloadResolution (
1988+ BinaryOperatorKind kind ,
1989+ bool isChecked ,
1990+ string name1 ,
1991+ string name2Opt ,
1992+ BoundExpression left ,
1993+ BoundExpression right ,
1994+ CSharpSyntaxNode node ,
1995+ BindingDiagnosticBag diagnostics ,
1996+ out LookupResultKind resultKind ,
1997+ out ImmutableArray < MethodSymbol > originalUserDefinedOperators )
1998+ {
1999+ resultKind = LookupResultKind . Empty ;
2000+ originalUserDefinedOperators = [ ] ;
2001+
2002+ if ( left . Type is null && right . Type is null )
19482003 {
1949- resultKind = LookupResultKind . Empty ;
1950- originalUserDefinedOperators = [ ] ;
2004+ return null ;
2005+ }
19512006
1952- if ( left . Type is null && right . Type is null )
1953- {
1954- return null ;
1955- }
2007+ var result = BinaryOperatorOverloadResolutionResult . GetInstance ( ) ;
2008+ CompoundUseSiteInfo < AssemblySymbol > useSiteInfo = GetNewCompoundUseSiteInfo ( diagnostics ) ;
2009+ var extensionDeclarationsInSingleScope = ArrayBuilder < NamedTypeSymbol > . GetInstance ( ) ;
2010+ BinaryOperatorAnalysisResult ? possiblyBest = null ;
19562011
1957- var result = BinaryOperatorOverloadResolutionResult . GetInstance ( ) ;
1958- CompoundUseSiteInfo < AssemblySymbol > useSiteInfo = GetNewCompoundUseSiteInfo ( diagnostics ) ;
1959- var extensionDeclarationsInSingleScope = ArrayBuilder < NamedTypeSymbol > . GetInstance ( ) ;
1960- BinaryOperatorAnalysisResult ? possiblyBest = null ;
2012+ foreach ( var scope in new ExtensionScopes ( this ) )
2013+ {
2014+ extensionDeclarationsInSingleScope . Clear ( ) ;
2015+ scope . Binder . GetExtensionDeclarations ( extensionDeclarationsInSingleScope , this ) ;
19612016
1962- foreach ( var scope in new ExtensionScopes ( this ) )
2017+ if ( this . OverloadResolution . BinaryOperatorExtensionOverloadResolutionInSingleScope ( extensionDeclarationsInSingleScope , kind , isChecked , name1 , name2Opt , left , right , result , ref useSiteInfo ) )
19632018 {
1964- extensionDeclarationsInSingleScope . Clear ( ) ;
1965- scope . Binder . GetExtensionDeclarations ( extensionDeclarationsInSingleScope , this ) ;
1966-
1967- if ( this . OverloadResolution . BinaryOperatorExtensionOverloadResolutionInSingleScope ( extensionDeclarationsInSingleScope , kind , isChecked , name1 , name2Opt , left , right , result , ref useSiteInfo ) )
1968- {
1969- possiblyBest = BinaryOperatorAnalyzeOverloadResolutionResult ( result , out resultKind , out originalUserDefinedOperators ) ;
1970- break ;
1971- }
2019+ possiblyBest = BinaryOperatorAnalyzeOverloadResolutionResult ( result , out resultKind , out originalUserDefinedOperators ) ;
2020+ break ;
19722021 }
2022+ }
19732023
1974- diagnostics . Add ( node , useSiteInfo ) ;
2024+ diagnostics . Add ( node , useSiteInfo ) ;
19752025
1976- extensionDeclarationsInSingleScope . Free ( ) ;
1977- result . Free ( ) ;
1978- return possiblyBest ;
1979- }
2026+ extensionDeclarationsInSingleScope . Free ( ) ;
2027+ result . Free ( ) ;
2028+ return possiblyBest ;
2029+ }
19802030
19812031#nullable disable
1982- }
19832032
19842033 private BinaryOperatorAnalysisResult BinaryOperatorNonExtensionOverloadResolution (
19852034 BinaryOperatorKind kind ,
@@ -3758,18 +3807,18 @@ private bool CheckConstraintLanguageVersionAndRuntimeSupportForOperator(SyntaxNo
37583807
37593808 if ( Compilation . SourceModule != methodOpt . ContainingModule )
37603809 {
3761- if ( SyntaxFacts . IsCheckedOperator ( methodOpt . Name ) )
3810+ if ( methodOpt . GetIsNewExtensionMember ( ) )
3811+ {
3812+ result &= CheckFeatureAvailability ( node , MessageID . IDS_FeatureExtensions , diagnostics ) ;
3813+ }
3814+ else if ( SyntaxFacts . IsCheckedOperator ( methodOpt . Name ) )
37623815 {
37633816 result &= CheckFeatureAvailability ( node , MessageID . IDS_FeatureCheckedUserDefinedOperators , diagnostics ) ;
37643817 }
37653818 else if ( isUnsignedRightShift )
37663819 {
37673820 result &= CheckFeatureAvailability ( node , MessageID . IDS_FeatureUnsignedRightShift , diagnostics ) ;
37683821 }
3769- else if ( methodOpt . GetIsNewExtensionMember ( ) )
3770- {
3771- result &= CheckFeatureAvailability ( node , MessageID . IDS_FeatureExtensions , diagnostics ) ;
3772- }
37733822 }
37743823 }
37753824
0 commit comments