@@ -441,13 +441,18 @@ private static BoundExpression CreateAnonymousFunctionConversion(SyntaxNode synt
441441
442442 private BoundExpression CreateMethodGroupConversion ( SyntaxNode syntax , BoundExpression source , Conversion conversion , bool isCast , ConversionGroup ? conversionGroup , TypeSymbol destination , DiagnosticBag diagnostics )
443443 {
444- BoundMethodGroup group = FixMethodGroupWithTypeOrValue ( ( BoundMethodGroup ) source , conversion , diagnostics ) ;
444+ var originalGroup = source switch
445+ {
446+ BoundMethodGroup m => m ,
447+ BoundUnconvertedAddressOfOperator { Operand : { } m } => m ,
448+ _ => throw ExceptionUtilities . UnexpectedValue ( source ) ,
449+ } ;
450+ BoundMethodGroup group = FixMethodGroupWithTypeOrValue ( originalGroup , conversion , diagnostics ) ;
445451 BoundExpression ? receiverOpt = group . ReceiverOpt ;
446452 MethodSymbol ? method = conversion . Method ;
447453 bool hasErrors = false ;
448454
449- NamedTypeSymbol delegateType = ( NamedTypeSymbol ) destination ;
450- if ( MethodGroupConversionHasErrors ( syntax , conversion , group . ReceiverOpt , conversion . IsExtensionMethod , delegateType , diagnostics ) )
455+ if ( MethodGroupConversionHasErrors ( syntax , conversion , group . ReceiverOpt , conversion . IsExtensionMethod , destination , diagnostics ) )
451456 {
452457 hasErrors = true ;
453458 }
@@ -859,26 +864,31 @@ internal static bool WasImplicitReceiver([NotNullWhen(false)] BoundExpression? r
859864 /// <summary>
860865 /// This method implements the checks in spec section 15.2.
861866 /// </summary>
862- internal bool MethodGroupIsCompatibleWithDelegate ( BoundExpression ? receiverOpt , bool isExtensionMethod , MethodSymbol method , NamedTypeSymbol delegateType , Location errorLocation , DiagnosticBag diagnostics )
867+ internal bool MethodIsCompatibleWithDelegateOrFunctionPointer ( BoundExpression ? receiverOpt , bool isExtensionMethod , MethodSymbol method , TypeSymbol delegateType , Location errorLocation , DiagnosticBag diagnostics )
863868 {
864- Debug . Assert ( delegateType . TypeKind == TypeKind . Delegate ) ;
865- RoslynDebug . Assert ( ( object ) delegateType . DelegateInvokeMethod != null && ! delegateType . DelegateInvokeMethod . HasUseSiteError ,
866- "This method should only be called for valid delegate types." ) ;
869+ Debug . Assert ( delegateType is NamedTypeSymbol { TypeKind : TypeKind . Delegate , DelegateInvokeMethod : { HasUseSiteError : false } }
870+ || delegateType . TypeKind == TypeKind . FunctionPointer ,
871+ "This method should only be called for valid delegate or function pointer types." ) ;
867872
868- MethodSymbol delegateMethod = delegateType . DelegateInvokeMethod ;
873+ MethodSymbol delegateOrFuncPtrMethod = delegateType switch
874+ {
875+ NamedTypeSymbol { DelegateInvokeMethod : { } invokeMethod } => invokeMethod ,
876+ FunctionPointerTypeSymbol { Signature : { } signature } => signature ,
877+ _ => throw ExceptionUtilities . UnexpectedValue ( delegateType ) ,
878+ } ;
869879
870880 Debug . Assert ( ! isExtensionMethod || ( receiverOpt != null ) ) ;
871881
872882 // - Argument types "match", and
873- var delegateParameters = delegateMethod . Parameters ;
883+ var delegateOrFuncPtrParameters = delegateOrFuncPtrMethod . Parameters ;
874884 var methodParameters = method . Parameters ;
875- int numParams = delegateParameters . Length ;
885+ int numParams = delegateOrFuncPtrParameters . Length ;
876886
877887 if ( methodParameters . Length != numParams + ( isExtensionMethod ? 1 : 0 ) )
878888 {
879889 // This can happen if "method" has optional parameters.
880890 Debug . Assert ( methodParameters . Length > numParams + ( isExtensionMethod ? 1 : 0 ) ) ;
881- Error ( diagnostics , ErrorCode . ERR_MethDelegateMismatch , errorLocation , method , delegateType ) ;
891+ Error ( diagnostics , getMethodMismatchErrorCode ( delegateType . TypeKind ) , errorLocation , method , delegateType ) ;
882892 return false ;
883893 }
884894
@@ -893,34 +903,44 @@ internal bool MethodGroupIsCompatibleWithDelegate(BoundExpression? receiverOpt,
893903
894904 for ( int i = 0 ; i < numParams ; i ++ )
895905 {
896- var delegateParameter = delegateParameters [ i ] ;
906+ var delegateParameter = delegateOrFuncPtrParameters [ i ] ;
897907 var methodParameter = methodParameters [ isExtensionMethod ? i + 1 : i ] ;
898908
899- if ( delegateParameter . RefKind != methodParameter . RefKind ||
900- ! Conversions . HasIdentityOrImplicitReferenceConversion ( delegateParameter . Type , methodParameter . Type , ref useSiteDiagnostics ) )
909+ // The delegate compatibility checks are stricter than the checks on applicable functions: it's possible
910+ // to get here with a method that, while all the parameters are applicable, is not actually delegate
911+ // compatible. This is because the Applicable function member spec requires that:
912+ // * Every value parameter (non-ref or similar) from the delegate type has an implicit conversion to the corresponding
913+ // target parameter
914+ // * Every ref or similar parameter has an identity conversion to the corresponding target parameter
915+ // However, the delegate compatiblity requirements are stricter:
916+ // * Every value parameter (non-ref or similar) from the delegate type has an implicit _reference_ conversion to the
917+ // corresponding target parameter.
918+ // * Every ref or similar parameter has an identity conversion to the corresponding target parameter
919+ // Note the addition of the reference requirement: it means that for delegate type void D(int i), void M(long l) is
920+ // _applicable_, but not _compatible_.
921+ if ( ! hasConversion ( delegateType . TypeKind , Conversions , delegateParameter . Type , methodParameter . Type , delegateParameter . RefKind , methodParameter . RefKind , ref useSiteDiagnostics ) )
901922 {
902923 // No overload for '{0}' matches delegate '{1}'
903- Error ( diagnostics , ErrorCode . ERR_MethDelegateMismatch , errorLocation , method , delegateType ) ;
924+ Error ( diagnostics , getMethodMismatchErrorCode ( delegateType . TypeKind ) , errorLocation , method , delegateType ) ;
904925 diagnostics . Add ( errorLocation , useSiteDiagnostics ) ;
905926 return false ;
906927 }
907928 }
908929
909- if ( delegateMethod . RefKind != method . RefKind )
930+ if ( delegateOrFuncPtrMethod . RefKind != method . RefKind )
910931 {
911- Error ( diagnostics , ErrorCode . ERR_DelegateRefMismatch , errorLocation , method , delegateType ) ;
932+ Error ( diagnostics , getRefMismatchErrorCode ( delegateType . TypeKind ) , errorLocation , method , delegateType ) ;
912933 diagnostics . Add ( errorLocation , useSiteDiagnostics ) ;
913934 return false ;
914935 }
915936
916937 var methodReturnType = method . ReturnType ;
917- var delegateReturnType = delegateMethod . ReturnType ;
918- bool returnsMatch = delegateMethod . RefKind != RefKind . None ?
919- // - Return types identity-convertible
920- Conversions . HasIdentityConversion ( methodReturnType , delegateReturnType ) :
921- // - Return types "match"
922- method . ReturnsVoid && delegateMethod . ReturnsVoid ||
923- Conversions . HasIdentityOrImplicitReferenceConversion ( methodReturnType , delegateReturnType , ref useSiteDiagnostics ) ;
938+ var delegateReturnType = delegateOrFuncPtrMethod . ReturnType ;
939+ bool returnsMatch = delegateOrFuncPtrMethod switch
940+ {
941+ { RefKind : RefKind . None , ReturnsVoid : true } => method . ReturnsVoid ,
942+ { RefKind : var destinationRefKind } => hasConversion ( delegateType . TypeKind , Conversions , methodReturnType , delegateReturnType , method . RefKind , destinationRefKind , ref useSiteDiagnostics ) ,
943+ } ;
924944
925945 if ( ! returnsMatch )
926946 {
@@ -929,8 +949,68 @@ internal bool MethodGroupIsCompatibleWithDelegate(BoundExpression? receiverOpt,
929949 return false ;
930950 }
931951
952+ if ( delegateType . IsFunctionPointer ( ) )
953+ {
954+ if ( isExtensionMethod )
955+ {
956+ Error ( diagnostics , ErrorCode . ERR_CannotUseReducedExtensionMethodInAddressOf , errorLocation ) ;
957+ diagnostics . Add ( errorLocation , useSiteDiagnostics ) ;
958+ return false ;
959+ }
960+
961+ if ( ! method . IsStatic )
962+ {
963+ // This check is here purely for completeness of implementing the spec. It should
964+ // never be hit, as static methods should be eliminated as candidates in overload
965+ // resolution and should never make it to this point.
966+ Debug . Fail ( "This method should have been eliminated in overload resolution!" ) ;
967+ Error ( diagnostics , ErrorCode . ERR_FuncPtrMethMustBeStatic , errorLocation , method ) ;
968+ diagnostics . Add ( errorLocation , useSiteDiagnostics ) ;
969+ return false ;
970+ }
971+ }
972+
932973 diagnostics . Add ( errorLocation , useSiteDiagnostics ) ;
933974 return true ;
975+
976+ static bool hasConversion ( TypeKind targetKind , Conversions conversions , TypeSymbol source , TypeSymbol destination ,
977+ RefKind sourceRefKind , RefKind destinationRefKind , ref HashSet < DiagnosticInfo > ? useSiteDiagnostics )
978+ {
979+ if ( sourceRefKind != destinationRefKind )
980+ {
981+ return false ;
982+ }
983+
984+ if ( sourceRefKind != RefKind . None )
985+ {
986+ return ConversionsBase . HasIdentityConversion ( source , destination ) ;
987+ }
988+
989+ if ( conversions . HasIdentityOrImplicitReferenceConversion ( source , destination , ref useSiteDiagnostics ) )
990+ {
991+ return true ;
992+ }
993+
994+ return targetKind == TypeKind . FunctionPointer
995+ && ( ConversionsBase . HasImplicitPointerToVoidConversion ( source , destination )
996+ || conversions . HasImplicitPointerConversion ( source , destination , ref useSiteDiagnostics ) ) ;
997+ }
998+
999+ static ErrorCode getMethodMismatchErrorCode ( TypeKind type )
1000+ => type switch
1001+ {
1002+ TypeKind . Delegate => ErrorCode . ERR_MethDelegateMismatch ,
1003+ TypeKind . FunctionPointer => ErrorCode . ERR_MethFuncPtrMismatch ,
1004+ _ => throw ExceptionUtilities . UnexpectedValue ( type )
1005+ } ;
1006+
1007+ static ErrorCode getRefMismatchErrorCode ( TypeKind type )
1008+ => type switch
1009+ {
1010+ TypeKind . Delegate => ErrorCode . ERR_DelegateRefMismatch ,
1011+ TypeKind . FunctionPointer => ErrorCode . ERR_FuncPtrRefMismatch ,
1012+ _ => throw ExceptionUtilities . UnexpectedValue ( type )
1013+ } ;
9341014 }
9351015
9361016 /// <summary>
@@ -940,23 +1020,23 @@ internal bool MethodGroupIsCompatibleWithDelegate(BoundExpression? receiverOpt,
9401020 /// <param name="conversion">Conversion to be performed.</param>
9411021 /// <param name="receiverOpt">Optional receiver.</param>
9421022 /// <param name="isExtensionMethod">Method invoked as extension method.</param>
943- /// <param name="delegateType ">Target delegate type.</param>
1023+ /// <param name="delegateOrFuncPtrType ">Target delegate type.</param>
9441024 /// <param name="diagnostics">Where diagnostics should be added.</param>
9451025 /// <returns>True if a diagnostic has been added.</returns>
9461026 private bool MethodGroupConversionHasErrors (
9471027 SyntaxNode syntax ,
9481028 Conversion conversion ,
9491029 BoundExpression ? receiverOpt ,
9501030 bool isExtensionMethod ,
951- NamedTypeSymbol delegateType ,
1031+ TypeSymbol delegateOrFuncPtrType ,
9521032 DiagnosticBag diagnostics )
9531033 {
954- Debug . Assert ( delegateType . TypeKind == TypeKind . Delegate ) ;
1034+ Debug . Assert ( delegateOrFuncPtrType . TypeKind == TypeKind . Delegate || delegateOrFuncPtrType . TypeKind == TypeKind . FunctionPointer ) ;
9551035
9561036 Debug . Assert ( conversion . Method is object ) ;
9571037 MethodSymbol selectedMethod = conversion . Method ;
9581038
959- if ( ! MethodGroupIsCompatibleWithDelegate ( receiverOpt , isExtensionMethod , selectedMethod , delegateType , syntax . Location , diagnostics ) ||
1039+ if ( ! MethodIsCompatibleWithDelegateOrFunctionPointer ( receiverOpt , isExtensionMethod , selectedMethod , delegateOrFuncPtrType , syntax . Location , diagnostics ) ||
9601040 MemberGroupFinalValidation ( receiverOpt , selectedMethod , syntax , diagnostics , isExtensionMethod ) )
9611041 {
9621042 return true ;
@@ -1008,11 +1088,11 @@ private bool MethodGroupConversionDoesNotExistOrHasErrors(
10081088 }
10091089
10101090 HashSet < DiagnosticInfo > ? useSiteDiagnostics = null ;
1011- conversion = Conversions . GetMethodGroupConversion ( boundMethodGroup , delegateType , ref useSiteDiagnostics ) ;
1091+ conversion = Conversions . GetMethodGroupDelegateConversion ( boundMethodGroup , delegateType , ref useSiteDiagnostics ) ;
10121092 diagnostics . Add ( delegateMismatchLocation , useSiteDiagnostics ) ;
10131093 if ( ! conversion . Exists )
10141094 {
1015- if ( ! Conversions . ReportDelegateMethodGroupDiagnostics ( this , boundMethodGroup , delegateType , diagnostics ) )
1095+ if ( ! Conversions . ReportDelegateOrFunctionPointerMethodGroupDiagnostics ( this , boundMethodGroup , delegateType , diagnostics ) )
10161096 {
10171097 // No overload for '{0}' matches delegate '{1}'
10181098 diagnostics . Add ( ErrorCode . ERR_MethDelegateMismatch , delegateMismatchLocation , boundMethodGroup . Name , delegateType ) ;
0 commit comments