From d2cfaf70d3e61e877842054d17b7516b2d33be23 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 4 Jan 2021 20:39:24 -0800 Subject: [PATCH 01/12] Implement lower and upper bound inference in function pointer parameters https://github.com/dotnet/roslyn/pull/43041 implemented exact inference of function pointer parameters and bounds only, and left a few errors in our tests that needed to be handled. This is also the cause of https://github.com/dotnet/roslyn/issues/50096. This implements bounds inference, using the same variance rules that function pointer to function pointer conversions do. I still need to spec the rules here, and may need to make a couple of adjustments around handling of delegate*->void* in bounds inference. At least initially, this isn't supported, but I'll work out what the correct rules should be when I spec it and update this PR accordingly. Fixes https://github.com/dotnet/roslyn/issues/50096. --- .../Semantics/Conversions/Conversions.cs | 6 +- .../OverloadResolution/MethodTypeInference.cs | 211 +++++++++++++++--- .../Portable/Symbols/TypeSymbolExtensions.cs | 19 ++ .../Semantics/FunctionPointerTests.cs | 208 +++++++++++++++-- 4 files changed, 391 insertions(+), 53 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index f49dc84b4db1..1225ec8015c3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -96,7 +96,7 @@ private static MethodGroupResolution ResolveDelegateOrFunctionPointerMethodGroup if ((object)delegateInvokeMethodOpt != null) { var analyzedArguments = AnalyzedArguments.GetInstance(); - GetDelegateArguments(source.Syntax, analyzedArguments, delegateInvokeMethodOpt.Parameters, binder.Compilation); + GetDelegateOrFunctionPointerArguments(source.Syntax, analyzedArguments, delegateInvokeMethodOpt.Parameters, binder.Compilation); var resolution = binder.ResolveMethodGroup(source, analyzedArguments, useSiteDiagnostics: ref useSiteDiagnostics, inferWithDynamic: true, isMethodGroupConversion: true, returnRefKind: delegateInvokeMethodOpt.RefKind, returnType: delegateInvokeMethodOpt.ReturnType, isFunctionPointerResolution: isFunctionPointer, callingConventionInfo: callingConventionInfo); @@ -229,7 +229,7 @@ public Conversion MethodGroupConversion(SyntaxNode syntax, MethodGroup methodGro Debug.Assert((object)delegateInvokeMethod != null && !delegateInvokeMethod.HasUseSiteError, "This method should only be called for valid delegate types"); - GetDelegateArguments(syntax, analyzedArguments, delegateInvokeMethod.Parameters, Compilation); + GetDelegateOrFunctionPointerArguments(syntax, analyzedArguments, delegateInvokeMethod.Parameters, Compilation); _binder.OverloadResolution.MethodInvocationOverloadResolution( methods: methodGroup.Methods, typeArguments: methodGroup.TypeArguments, @@ -247,7 +247,7 @@ public Conversion MethodGroupConversion(SyntaxNode syntax, MethodGroup methodGro return conversion; } - public static void GetDelegateArguments(SyntaxNode syntax, AnalyzedArguments analyzedArguments, ImmutableArray delegateParameters, CSharpCompilation compilation) + public static void GetDelegateOrFunctionPointerArguments(SyntaxNode syntax, AnalyzedArguments analyzedArguments, ImmutableArray delegateParameters, CSharpCompilation compilation) { foreach (var p in delegateParameters) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 63cbbbc8227f..4347004300b8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -501,10 +501,10 @@ private bool HasBound(int methodTypeParameterIndex) _exactBounds[methodTypeParameterIndex] != null; } - private NamedTypeSymbol GetFixedDelegate(NamedTypeSymbol delegateType) + private TypeSymbol GetFixedDelegateOrFunctionPointer(TypeSymbol delegateType) { Debug.Assert((object)delegateType != null); - Debug.Assert(delegateType.IsDelegateType()); + Debug.Assert(delegateType.IsDelegateType() || delegateType is FunctionPointerTypeSymbol); // We have a delegate where the input types use no unfixed parameters. Create // a substitution context; we can substitute unfixed parameters for themselves @@ -519,7 +519,7 @@ private NamedTypeSymbol GetFixedDelegate(NamedTypeSymbol delegateType) } TypeMap typeMap = new TypeMap(_constructedContainingTypeOfMethod, _methodTypeParameters, fixedArguments.ToImmutableAndFree()); - return typeMap.SubstituteNamedType(delegateType); + return typeMap.SubstituteType(delegateType).Type; } //////////////////////////////////////////////////////////////////////////////// @@ -558,7 +558,7 @@ private void InferTypeArgsFirstPhase(ref HashSet useSiteDiagnost { BoundExpression argument = _arguments[arg]; TypeWithAnnotations target = _formalParameterTypes[arg]; - ExactOrBoundsKind kind = GetRefKind(arg).IsManagedReference() || target.Type.IsPointerOrFunctionPointer() ? ExactOrBoundsKind.Exact : ExactOrBoundsKind.LowerBound; + ExactOrBoundsKind kind = GetRefKind(arg).IsManagedReference() || target.Type.IsPointerType() ? ExactOrBoundsKind.Exact : ExactOrBoundsKind.LowerBound; MakeExplicitParameterTypeInferences(argument, target, kind, ref useSiteDiagnostics); } @@ -861,26 +861,26 @@ private static bool DoesInputTypeContain(BoundExpression argument, TypeSymbol fo // SPEC: type or expression tree type then all the parameter types of T are // SPEC: input types of E with type T. - var delegateType = formalParameterType.GetDelegateType(); - if ((object)delegateType == null) + var delegateOrFunctionPointerType = formalParameterType.GetDelegateOrFunctionPointerType(); + if ((object)delegateOrFunctionPointerType == null) { return false; // No input types. } - if (argument.Kind != BoundKind.UnboundLambda && argument.Kind != BoundKind.MethodGroup) + if (argument.Kind is not (BoundKind.UnboundLambda or BoundKind.MethodGroup or BoundKind.UnconvertedAddressOfOperator)) { return false; // No input types. } - var delegateParameters = delegateType.DelegateParameters(); - if (delegateParameters.IsDefaultOrEmpty) + var parameters = delegateOrFunctionPointerType.DelegateOrFunctionPointerParameters(); + if (parameters.IsDefaultOrEmpty) { return false; } - foreach (var delegateParameter in delegateParameters) + foreach (var parameter in parameters) { - if (delegateParameter.Type.ContainsTypeParameter(typeParameter)) + if (parameter.Type.ContainsTypeParameter(typeParameter)) { return true; } @@ -915,30 +915,36 @@ private static bool DoesOutputTypeContain(BoundExpression argument, TypeSymbol f // SPEC: type or expression tree type then the return type of T is an output type // SPEC: of E with type T. - var delegateType = formalParameterType.GetDelegateType(); - if ((object)delegateType == null) + var delegateOrFunctionPointerType = formalParameterType.GetDelegateOrFunctionPointerType(); + if ((object)delegateOrFunctionPointerType == null) { return false; } - if (argument.Kind != BoundKind.UnboundLambda && argument.Kind != BoundKind.MethodGroup) + if (argument.Kind is not (BoundKind.UnboundLambda or BoundKind.MethodGroup or BoundKind.UnconvertedAddressOfOperator)) { return false; } - MethodSymbol delegateInvoke = delegateType.DelegateInvokeMethod; - if ((object)delegateInvoke == null || delegateInvoke.HasUseSiteError) + MethodSymbol method = delegateOrFunctionPointerType switch + { + NamedTypeSymbol n => n.DelegateInvokeMethod, + FunctionPointerTypeSymbol f => f.Signature, + _ => throw ExceptionUtilities.UnexpectedValue(delegateOrFunctionPointerType) + }; + + if ((object)method == null || method.HasUseSiteError) { return false; } - var delegateReturnType = delegateInvoke.ReturnType; - if ((object)delegateReturnType == null) + var returnType = method.ReturnType; + if ((object)returnType == null) { return false; } - return delegateReturnType.ContainsTypeParameter(typeParameter); + return returnType.ContainsTypeParameter(typeParameter); } private bool HasUnfixedParamInOutputType(BoundExpression argument, TypeSymbol formalParameterType) @@ -1281,44 +1287,54 @@ private bool MethodGroupReturnTypeInference(Binder binder, BoundExpression sourc // SPEC: yields a single method with return type U then a lower-bound // SPEC: inference is made from U to Tb. - if (source.Kind != BoundKind.MethodGroup) + if (source.Kind is not (BoundKind.MethodGroup or BoundKind.UnconvertedAddressOfOperator)) { return false; } - var delegateType = target.GetDelegateType(); - if ((object)delegateType == null) + var delegateOrFunctionPointerType = target.GetDelegateOrFunctionPointerType(); + if ((object)delegateOrFunctionPointerType == null) { return false; } // this part of the code is only called if the targetType has an unfixed type argument in the output // type, which is not the case for invalid delegate invoke methods. - var delegateInvokeMethod = delegateType.DelegateInvokeMethod; - Debug.Assert((object)delegateInvokeMethod != null && !delegateType.DelegateInvokeMethod.HasUseSiteError, - "This method should only be called for valid delegate types"); + var (method, isFunctionPointerResolution) = delegateOrFunctionPointerType switch + { + NamedTypeSymbol n => (n.DelegateInvokeMethod, false), + FunctionPointerTypeSymbol f => (f.Signature, true), + _ => throw ExceptionUtilities.UnexpectedValue(delegateOrFunctionPointerType), + }; + Debug.Assert(method is { HasUseSiteError: false }, + "This method should only be called for valid delegate or function pointer types"); - TypeWithAnnotations delegateReturnType = delegateInvokeMethod.ReturnTypeWithAnnotations; - if (!delegateReturnType.HasType || delegateReturnType.SpecialType == SpecialType.System_Void) + TypeWithAnnotations sourceReturnType = method.ReturnTypeWithAnnotations; + if (!sourceReturnType.HasType || sourceReturnType.SpecialType == SpecialType.System_Void) { return false; } // At this point we are in the second phase; we know that all the input types are fixed. - var fixedDelegateParameters = GetFixedDelegate(delegateType).DelegateParameters(); - if (fixedDelegateParameters.IsDefault) + var fixedParameters = GetFixedDelegateOrFunctionPointer(delegateOrFunctionPointerType).DelegateOrFunctionPointerParameters(); + if (fixedParameters.IsDefault) { return false; } - var returnType = MethodGroupReturnType(binder, (BoundMethodGroup)source, fixedDelegateParameters, delegateInvokeMethod.RefKind, ref useSiteDiagnostics); + CallingConventionInfo callingConventionInfo = isFunctionPointerResolution + ? new CallingConventionInfo(method.CallingConvention, ((FunctionPointerMethodSymbol)method).GetCallingConventionModifiers()) + : default; + BoundMethodGroup originalMethodGroup = source as BoundMethodGroup ?? ((BoundUnconvertedAddressOfOperator)source).Operand; + + var returnType = MethodGroupReturnType(binder, originalMethodGroup, fixedParameters, method.RefKind, isFunctionPointerResolution, ref useSiteDiagnostics, in callingConventionInfo); if (returnType.IsDefault || returnType.IsVoidType()) { return false; } - LowerBoundInference(returnType, delegateReturnType, ref useSiteDiagnostics); + LowerBoundInference(returnType, sourceReturnType, ref useSiteDiagnostics); return true; } @@ -1326,15 +1342,18 @@ private TypeWithAnnotations MethodGroupReturnType( Binder binder, BoundMethodGroup source, ImmutableArray delegateParameters, RefKind delegateRefKind, - ref HashSet useSiteDiagnostics) + bool isFunctionPointerResolution, + ref HashSet useSiteDiagnostics, + in CallingConventionInfo callingConventionInfo) { var analyzedArguments = AnalyzedArguments.GetInstance(); - Conversions.GetDelegateArguments(source.Syntax, analyzedArguments, delegateParameters, binder.Compilation); + Conversions.GetDelegateOrFunctionPointerArguments(source.Syntax, analyzedArguments, delegateParameters, binder.Compilation); var resolution = binder.ResolveMethodGroup(source, analyzedArguments, useSiteDiagnostics: ref useSiteDiagnostics, isMethodGroupConversion: true, returnRefKind: delegateRefKind, // Since we are trying to infer the return type, it is not an input to resolving the method group - returnType: null); + returnType: null, + isFunctionPointerResolution: isFunctionPointerResolution, callingConventionInfo: in callingConventionInfo); TypeWithAnnotations type = default; @@ -1739,6 +1758,11 @@ private void LowerBoundInference(TypeWithAnnotations source, TypeWithAnnotations return; } + if (LowerBoundFunctionPointerTypeInference(source.Type, target.Type, ref useSiteDiagnostics)) + { + return; + } + // SPEC: * Otherwise, no inferences are made. } @@ -2067,6 +2091,61 @@ private void LowerBoundTypeArgumentInference(NamedTypeSymbol source, NamedTypeSy targetTypeArguments.Free(); } +#nullable enable + private bool LowerBoundFunctionPointerTypeInference(TypeSymbol source, TypeSymbol target, ref HashSet useSiteDiagnostics) + { + if (source is not FunctionPointerTypeSymbol { Signature: { } sourceSignature } || target is not FunctionPointerTypeSymbol { Signature: { } targetSignature }) + { + return false; + } + + if (sourceSignature.ParameterCount != targetSignature.ParameterCount) + { + return false; + } + + if (sourceSignature.RefKind != targetSignature.RefKind) + { + return false; + } + + if (sourceSignature.ParameterRefKinds.IsDefault + ? !targetSignature.ParameterRefKinds.IsDefault + : !sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds)) + { + return false; + } + + // Reference parameters are treated as "input" variance by default, and reference return types are treated as out variance by default. + // If they have a ref kind or are not reference types, then they are treated as invariant. + for (int i = 0; i < sourceSignature.ParameterCount; i++) + { + var sourceParam = sourceSignature.Parameters[i]; + var targetParam = targetSignature.Parameters[i]; + + if ((sourceParam.Type.IsReferenceType || sourceParam.Type.IsFunctionPointer()) && sourceParam.RefKind == RefKind.None) + { + UpperBoundInference(sourceParam.TypeWithAnnotations, targetParam.TypeWithAnnotations, ref useSiteDiagnostics); + } + else + { + ExactInference(sourceParam.TypeWithAnnotations, targetParam.TypeWithAnnotations, ref useSiteDiagnostics); + } + } + + if ((sourceSignature.ReturnType.IsReferenceType || sourceSignature.ReturnType.IsFunctionPointer()) && sourceSignature.RefKind == RefKind.None) + { + LowerBoundInference(sourceSignature.ReturnTypeWithAnnotations, targetSignature.ReturnTypeWithAnnotations, ref useSiteDiagnostics); + } + else + { + ExactInference(sourceSignature.ReturnTypeWithAnnotations, targetSignature.ReturnTypeWithAnnotations, ref useSiteDiagnostics); + } + + return true; + } +#nullable disable + //////////////////////////////////////////////////////////////////////////////// // // Upper-bound inferences @@ -2107,7 +2186,7 @@ private void UpperBoundInference(TypeWithAnnotations source, TypeWithAnnotations return; } - Debug.Assert(source.Type.IsReferenceType); + Debug.Assert(source.Type.IsReferenceType || source.Type.IsFunctionPointer()); // NOTE: spec would ask us to do the following checks, but since the value types // are trivially handled as exact inference in the callers, we do not have to. @@ -2124,6 +2203,11 @@ private void UpperBoundInference(TypeWithAnnotations source, TypeWithAnnotations return; } + if (UpperBoundFunctionPointerTypeInference(source.Type, target.Type, ref useSiteDiagnostics)) + { + return; + } + // SPEC: * Otherwise, no inferences are made. } @@ -2364,6 +2448,61 @@ private void UpperBoundTypeArgumentInference(NamedTypeSymbol source, NamedTypeSy targetTypeArguments.Free(); } +#nullable enable + private bool UpperBoundFunctionPointerTypeInference(TypeSymbol source, TypeSymbol target, ref HashSet useSiteDiagnostics) + { + if (source is not FunctionPointerTypeSymbol { Signature: { } sourceSignature } || target is not FunctionPointerTypeSymbol { Signature: { } targetSignature }) + { + return false; + } + + if (sourceSignature.ParameterCount != targetSignature.ParameterCount) + { + return false; + } + + if (sourceSignature.RefKind != targetSignature.RefKind) + { + return false; + } + + if (sourceSignature.ParameterRefKinds.IsDefault + ? !targetSignature.ParameterRefKinds.IsDefault + : !sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds)) + { + return false; + } + + // Reference parameters are treated as "input" variance by default, and reference return types are treated as out variance by default. + // If they have a ref kind or are not reference types, then they are treated as invariant. + for (int i = 0; i < sourceSignature.ParameterCount; i++) + { + var sourceParam = sourceSignature.Parameters[i]; + var targetParam = targetSignature.Parameters[i]; + + if ((sourceParam.Type.IsReferenceType || sourceParam.Type.IsFunctionPointer()) && sourceParam.RefKind == RefKind.None) + { + LowerBoundInference(sourceParam.TypeWithAnnotations, targetParam.TypeWithAnnotations, ref useSiteDiagnostics); + } + else + { + ExactInference(sourceParam.TypeWithAnnotations, targetParam.TypeWithAnnotations, ref useSiteDiagnostics); + } + } + + if ((sourceSignature.ReturnType.IsReferenceType || sourceSignature.ReturnType.IsFunctionPointer()) && sourceSignature.RefKind == RefKind.None) + { + UpperBoundInference(sourceSignature.ReturnTypeWithAnnotations, targetSignature.ReturnTypeWithAnnotations, ref useSiteDiagnostics); + } + else + { + ExactInference(sourceSignature.ReturnTypeWithAnnotations, targetSignature.ReturnTypeWithAnnotations, ref useSiteDiagnostics); + } + + return true; + } +#nullable disable + //////////////////////////////////////////////////////////////////////////////// // // Fixing @@ -2589,7 +2728,7 @@ private TypeWithAnnotations InferReturnType(BoundExpression source, NamedTypeSym } } - var fixedDelegate = GetFixedDelegate(target); + var fixedDelegate = (NamedTypeSymbol)GetFixedDelegateOrFunctionPointer(target); var fixedDelegateParameters = fixedDelegate.DelegateParameters(); // Optimization: // Similarly, if we have an entirely fixed delegate and an explicitly typed diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 80de90fa7dbf..2e1c6577e952 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -376,6 +377,11 @@ public static bool IsPointerOrFunctionPointer(this TypeSymbol type) return type.IsDelegateType() ? (NamedTypeSymbol)type : null; } + public static TypeSymbol? GetDelegateOrFunctionPointerType(this TypeSymbol type) + { + return (TypeSymbol?)GetDelegateType(type) ?? type as FunctionPointerTypeSymbol; + } + /// /// return true if the type is constructed from System.Linq.Expressions.Expression`1 /// @@ -445,6 +451,19 @@ public static ImmutableArray DelegateParameters(this TypeSymbol return invokeMethod.Parameters; } + public static ImmutableArray DelegateOrFunctionPointerParameters(this TypeSymbol type) + { + Debug.Assert(type is FunctionPointerTypeSymbol || type.IsDelegateType()); + if (type is FunctionPointerTypeSymbol { Signature: { Parameters: var functionPointerParameters } }) + { + return functionPointerParameters; + } + else + { + return type.DelegateParameters(); + } + } + public static bool TryGetElementTypesWithAnnotationsIfTupleType(this TypeSymbol type, out ImmutableArray elementTypes) { if (type.IsTupleType) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index c1bfbea26cfa..4a55604138e9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -1357,12 +1357,64 @@ public void M2() } }"); - // This should be inferrable with variant conversions, tracked by https://github.com/dotnet/roslyn/issues/39865 - comp.VerifyDiagnostics( - // (9,9): error CS0411: The type arguments for method 'C.M1(delegate*, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // M1(p1, p2); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*, delegate*)").WithLocation(9, 9) - ); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + + AssertEx.Equal("void C.M1(delegate* param1, delegate* param2)", + model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void FunctionPointerGenericSubstitutionVariantParameterSubstitutions_UpperBounds_Parameter() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class C +{ + public void M1(delegate*, void> param1, delegate*, void> param2) {} + public void M2() + { + delegate*, void> p1 = null; + delegate*, void> p2 = null; + M1(p1, p2); + } +}"); + + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + + AssertEx.Equal("void C.M1(delegate*, System.Void> param1, delegate*, System.Void> param2)", + model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void FunctionPointerGenericSubstitutionVariantParameterSubstitutions_UpperBounds_Return() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class C +{ + public void M1(delegate*, void> param1, delegate*, void> param2) {} + public void M2() + { + delegate*, void> p1 = null; + delegate*, void> p2 = null; + M1(p1, p2); + } +}"); + + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + + AssertEx.Equal("void C.M1(delegate*, System.Void> param1, delegate*, System.Void> param2)", + model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); } [Fact] @@ -1441,12 +1493,64 @@ public void M2() } }"); - // This should be inferrable with variant conversions, tracked by https://github.com/dotnet/roslyn/issues/39865 - comp.VerifyDiagnostics( - // (9,9): error CS0411: The type arguments for method 'C.M1(delegate*, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // M1(p1, p2); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*, delegate*)").WithLocation(9, 9) - ); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + + AssertEx.Equal("void C.M1(delegate* param1, delegate* param2)", + model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void FunctionPointerGenericSubstitutionVariantReturnSubstitutions_LowerBounds_Parameter() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class C +{ + public void M1(delegate*> param1, delegate*> param2) {} + public void M2() + { + delegate*> p1 = null; + delegate*> p2 = null; + M1(p1, p2); + } +}"); + + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + + AssertEx.Equal("void C.M1(delegate*> param1, delegate*> param2)", + model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void FunctionPointerGenericSubstitutionVariantReturnSubstitutions_LowerBounds_Return() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class C +{ + public void M1(delegate*> param1, delegate*> param2) {} + public void M2() + { + delegate*> p1 = null; + delegate*> p2 = null; + M1(p1, p2); + } +}"); + + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + + AssertEx.Equal("void C.M1(delegate*> param1, delegate*> param2)", + model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); } [Fact] @@ -1563,7 +1667,6 @@ public static void M() } }"); - // Some of these errors should become CS0306 after variant conversions are implemented, tracked by https://github.com/dotnet/roslyn/issues/39865 comp.VerifyDiagnostics( // (3,14): error CS0306: The type 'delegate*' may not be used as a type argument // unsafe class D : C> @@ -1651,7 +1754,6 @@ public static void M() } }"); - // These should all work: https://github.com/dotnet/roslyn/issues/39865 comp.VerifyDiagnostics( // (10,9): error CS0411: The type arguments for method 'C.SubstitutedStatic2(TStatic, TStatic)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // SubstitutedStatic2(ptr1, ptr2); @@ -2069,6 +2171,84 @@ void M(bool b) AssertEx.Equal(expectedTypes, invocationTypes); } + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInferenceInParameter() + { + var verifier = CompileAndVerify(@" +unsafe +{ + Test(0, &converter); + + static string converter(int v) => string.Empty; + static void Test(T1 t1, delegate* func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + verifier.VerifyIL("", @" +{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldc.i4.0 + IL_0001: ldftn ""string $.<
$>g__converter|0_0(int)"" + IL_0007: call ""void $.<
$>g__Test|0_1(int, delegate*)"" + IL_000c: ret +} +"); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInferenceInParameter_ImplicitReferenceConversionOnParameter() + { + var verifier = CompileAndVerify(@" +unsafe +{ + Test(string.Empty, &converter); + + static string converter(object o) => string.Empty; + static void Test(T1 t1, delegate* func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + verifier.VerifyIL("", @" +{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldsfld ""string string.Empty"" + IL_0005: ldftn ""string $.<
$>g__converter|0_0(object)"" + IL_000b: call ""void $.<
$>g__Test|0_1(string, delegate*)"" + IL_0010: ret +} +"); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInferenceInReturn() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe +{ + Test(0, &converter); + + static int converter(string v) => 0; + static void Test(T2 t2, delegate* func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + // It might seem like this should have the same behavior as FunctionPointerInferenceInParameter. However, this is not how method group resolution works. + // We don't look at return types when resolving a method group, which means that we can't use T2 to narrow down the candidates. We therefore can't + // find any information about T1, and resolution fails. FunctionPointerInferenceInParameter, on the other hand, has a bound for T1: int. That bound + // is then used as an input to method group resolution, and we can narrow down to string converter(int v). The return type of that method can then be + // used as a bound to infer T2. + comp.VerifyDiagnostics( + // (4,5): error CS0411: The type arguments for method 'Test(T2, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test(0, &converter); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Test(T2, delegate*)").WithLocation(4, 5), + // (7,17): warning CS8321: The local function 'Test' is declared but never used + // static void Test(T2 t2, delegate* func) {} + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "Test").WithArguments("Test").WithLocation(7, 17) + ); + } + [Fact] public void FunctionPointerTypeCannotBeUsedInDynamicTypeArguments() { From 80ce5f872898b80317e53b0884f84dad6ecacfd0 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 7 Jan 2021 11:13:51 -0800 Subject: [PATCH 02/12] Add addtional testing for pointer conversions. `T` cannot appear variantly inside a pointer, and any inferences on such `T`s should be done with exact inference. --- .../Semantics/FunctionPointerTests.cs | 72 +++++++++++++++---- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index 4a55604138e9..e197f8850db1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -1354,14 +1354,22 @@ public void M2() delegate* p1 = null; delegate* p2 = null; M1(p1, p2); + + delegate* p3 = null; + delegate* p4 = null; + M1(p3, p4); } }"); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (13,9): error CS0411: The type arguments for method 'C.M1(delegate*, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M1(p3, p4); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*, delegate*)").WithLocation(13, 9) + ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().First(); AssertEx.Equal("void C.M1(delegate* param1, delegate* param2)", model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); @@ -1379,14 +1387,22 @@ public void M2() delegate*, void> p1 = null; delegate*, void> p2 = null; M1(p1, p2); + + delegate*, void> p3 = null; + delegate*, void> p4 = null; + M1(p3, p4); } }"); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (13,9): error CS0411: The type arguments for method 'C.M1(delegate*, void>, delegate*, void>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M1(p3, p4); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*, void>, delegate*, void>)").WithLocation(13, 9) + ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().First(); AssertEx.Equal("void C.M1(delegate*, System.Void> param1, delegate*, System.Void> param2)", model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); @@ -1404,14 +1420,22 @@ public void M2() delegate*, void> p1 = null; delegate*, void> p2 = null; M1(p1, p2); + + delegate*, void> p3 = null; + delegate*, void> p4 = null; + M1(p3, p4); } }"); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (13,9): error CS0411: The type arguments for method 'C.M1(delegate*, void>, delegate*, void>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M1(p3, p4); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*, void>, delegate*, void>)").WithLocation(13, 9) + ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().First(); AssertEx.Equal("void C.M1(delegate*, System.Void> param1, delegate*, System.Void> param2)", model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); @@ -1490,14 +1514,22 @@ public void M2() delegate* p1 = null; delegate* p2 = null; M1(p1, p2); + + delegate* p3 = null; + delegate* p4 = null; + M1(p3, p4); } }"); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (13,9): error CS0411: The type arguments for method 'C.M1(delegate*, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M1(p3, p4); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*, delegate*)").WithLocation(13, 9) + ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().First(); AssertEx.Equal("void C.M1(delegate* param1, delegate* param2)", model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); @@ -1515,14 +1547,22 @@ public void M2() delegate*> p1 = null; delegate*> p2 = null; M1(p1, p2); + + delegate*> p3 = null; + delegate*> p4 = null; + M1(p3, p4); } }"); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (13,9): error CS0411: The type arguments for method 'C.M1(delegate*>, delegate*>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M1(p3, p4); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*>, delegate*>)").WithLocation(13, 9) + ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().First(); AssertEx.Equal("void C.M1(delegate*> param1, delegate*> param2)", model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); @@ -1540,14 +1580,22 @@ public void M2() delegate*> p1 = null; delegate*> p2 = null; M1(p1, p2); + + delegate*> p3 = null; + delegate*> p4 = null; + M1(p3, p4); } }"); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (13,9): error CS0411: The type arguments for method 'C.M1(delegate*>, delegate*>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // M1(p3, p4); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1(delegate*>, delegate*>)").WithLocation(13, 9) + ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var m1Invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + var m1Invocation = tree.GetRoot().DescendantNodes().OfType().First(); AssertEx.Equal("void C.M1(delegate*> param1, delegate*> param2)", model.GetSymbolInfo(m1Invocation).Symbol.ToTestDisplayString()); From d8afd5a7b6104604a2fc25036df4124c34cdf1cd Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 7 Jan 2021 11:20:44 -0800 Subject: [PATCH 03/12] Skip unmanaged pointer peverify on framework. --- .../CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index e197f8850db1..cb84b4a705a9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -2230,7 +2230,7 @@ public void FunctionPointerInferenceInParameter() static string converter(int v) => string.Empty; static void Test(T1 t1, delegate* func) {} } -", options: TestOptions.UnsafeReleaseExe); +", options: TestOptions.UnsafeReleaseExe, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped); verifier.VerifyIL("", @" { @@ -2255,7 +2255,7 @@ public void FunctionPointerInferenceInParameter_ImplicitReferenceConversionOnPar static string converter(object o) => string.Empty; static void Test(T1 t1, delegate* func) {} } -", options: TestOptions.UnsafeReleaseExe); +", options: TestOptions.UnsafeReleaseExe, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped); verifier.VerifyIL("", @" { From f656b0643e4407ef1265b62c4b2ecab11102efcb Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 14 Jan 2021 18:27:58 -0800 Subject: [PATCH 04/12] PR feedback * Missed parameter rename. * Ensure error condition is explicity rejected. --- .../OverloadResolution/MethodTypeInference.cs | 22 +++++++++---- .../Semantics/FunctionPointerTests.cs | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 4347004300b8..30814fe1e821 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -501,10 +501,10 @@ private bool HasBound(int methodTypeParameterIndex) _exactBounds[methodTypeParameterIndex] != null; } - private TypeSymbol GetFixedDelegateOrFunctionPointer(TypeSymbol delegateType) + private TypeSymbol GetFixedDelegateOrFunctionPointer(TypeSymbol delegateOrFunctionPointerType) { - Debug.Assert((object)delegateType != null); - Debug.Assert(delegateType.IsDelegateType() || delegateType is FunctionPointerTypeSymbol); + Debug.Assert((object)delegateOrFunctionPointerType != null); + Debug.Assert(delegateOrFunctionPointerType.IsDelegateType() || delegateOrFunctionPointerType is FunctionPointerTypeSymbol); // We have a delegate where the input types use no unfixed parameters. Create // a substitution context; we can substitute unfixed parameters for themselves @@ -519,7 +519,7 @@ private TypeSymbol GetFixedDelegateOrFunctionPointer(TypeSymbol delegateType) } TypeMap typeMap = new TypeMap(_constructedContainingTypeOfMethod, _methodTypeParameters, fixedArguments.ToImmutableAndFree()); - return typeMap.SubstituteType(delegateType).Type; + return typeMap.SubstituteType(delegateOrFunctionPointerType).Type; } //////////////////////////////////////////////////////////////////////////////// @@ -867,7 +867,9 @@ private static bool DoesInputTypeContain(BoundExpression argument, TypeSymbol fo return false; // No input types. } - if (argument.Kind is not (BoundKind.UnboundLambda or BoundKind.MethodGroup or BoundKind.UnconvertedAddressOfOperator)) + var isFunctionPointer = delegateOrFunctionPointerType.IsFunctionPointer(); + if ((isFunctionPointer && argument.Kind != BoundKind.UnconvertedAddressOfOperator) || + (!isFunctionPointer && argument.Kind is not BoundKind.UnboundLambda or BoundKind.MethodGroup)) { return false; // No input types. } @@ -921,7 +923,9 @@ private static bool DoesOutputTypeContain(BoundExpression argument, TypeSymbol f return false; } - if (argument.Kind is not (BoundKind.UnboundLambda or BoundKind.MethodGroup or BoundKind.UnconvertedAddressOfOperator)) + var isFunctionPointer = delegateOrFunctionPointerType.IsFunctionPointer(); + if ((isFunctionPointer && argument.Kind != BoundKind.UnconvertedAddressOfOperator) || + (!isFunctionPointer && argument.Kind is not BoundKind.UnboundLambda or BoundKind.MethodGroup)) { return false; } @@ -1298,6 +1302,12 @@ private bool MethodGroupReturnTypeInference(Binder binder, BoundExpression sourc return false; } + + if (delegateOrFunctionPointerType.IsFunctionPointer() != (source.Kind == BoundKind.UnconvertedAddressOfOperator)) + { + return false; + } + // this part of the code is only called if the targetType has an unfixed type argument in the output // type, which is not the case for invalid delegate invoke methods. var (method, isFunctionPointerResolution) = delegateOrFunctionPointerType switch diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index cb84b4a705a9..63633d6df9ca 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -2297,6 +2297,37 @@ static void Test(T2 t2, delegate* func) {} ); } + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_ThroughMethodGroup() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe +{ + Test1(0, converter); + Test2(0, converter); + + static int converter(string v) => 0; + static void Test1(T1 t1, delegate* func) {} + static void Test2(T2 t2, delegate* func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + comp.VerifyDiagnostics( + // (4,5): error CS0411: The type arguments for method 'Test1(T1, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test1(0, converter); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(T1, delegate*)").WithLocation(4, 5), + // (5,5): error CS0411: The type arguments for method 'Test2(T2, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test2(0, converter); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test2").WithArguments("Test2(T2, delegate*)").WithLocation(5, 5), + // (8,17): warning CS8321: The local function 'Test1' is declared but never used + // static void Test1(T1 t1, delegate* func) {} + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "Test1").WithArguments("Test1").WithLocation(8, 17), + // (9,17): warning CS8321: The local function 'Test2' is declared but never used + // static void Test2(T2 t2, delegate* func) {} + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "Test2").WithArguments("Test2").WithLocation(9, 17) + ); + } + [Fact] public void FunctionPointerTypeCannotBeUsedInDynamicTypeArguments() { From f91e8d8f4a7d781c78fce44a94e1a2b0747f8936 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 15 Jan 2021 10:51:28 -0800 Subject: [PATCH 05/12] Add missing parentheses. --- .../Semantics/OverloadResolution/MethodTypeInference.cs | 4 ++-- .../Test/Semantic/Semantics/OverloadResolutionTestBase.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 30814fe1e821..6c7ec34a7e9d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -869,7 +869,7 @@ private static bool DoesInputTypeContain(BoundExpression argument, TypeSymbol fo var isFunctionPointer = delegateOrFunctionPointerType.IsFunctionPointer(); if ((isFunctionPointer && argument.Kind != BoundKind.UnconvertedAddressOfOperator) || - (!isFunctionPointer && argument.Kind is not BoundKind.UnboundLambda or BoundKind.MethodGroup)) + (!isFunctionPointer && argument.Kind is not (BoundKind.UnboundLambda or BoundKind.MethodGroup))) { return false; // No input types. } @@ -925,7 +925,7 @@ private static bool DoesOutputTypeContain(BoundExpression argument, TypeSymbol f var isFunctionPointer = delegateOrFunctionPointerType.IsFunctionPointer(); if ((isFunctionPointer && argument.Kind != BoundKind.UnconvertedAddressOfOperator) || - (!isFunctionPointer && argument.Kind is not BoundKind.UnboundLambda or BoundKind.MethodGroup)) + (!isFunctionPointer && argument.Kind is not (BoundKind.UnboundLambda or BoundKind.MethodGroup))) { return false; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTestBase.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTestBase.cs index 0a4243003568..3080d848d807 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTestBase.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTestBase.cs @@ -48,7 +48,7 @@ internal void TestOverloadResolutionWithDiff(string source, MetadataReference[] .Select(x => x.Substring(x.IndexOf("//-", StringComparison.Ordinal) + 3)) .ToArray()); - AssertEx.Equal(expected, results); + AssertEx.EqualOrDiff(expected, results); } } } From 35ce5d08d5f99cd998a617708c1c643b9eb953ab Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 15 Jan 2021 14:07:37 -0800 Subject: [PATCH 06/12] Add more testing. --- .../Semantics/FunctionPointerTests.cs | 84 +++++++++++++++++-- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index 63633d6df9ca..b6a14752d85f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -2303,8 +2303,10 @@ public void FunctionPointerInference_ThroughMethodGroup() var comp = CreateCompilationWithFunctionPointers(@" unsafe { - Test1(0, converter); + Test1(string.Empty, converter); Test2(0, converter); + Test1(string.Empty, converter); + Test2(0, converter); static int converter(string v) => 0; static void Test1(T1 t1, delegate* func) {} @@ -2314,17 +2316,83 @@ static void Test2(T2 t2, delegate* func) {} comp.VerifyDiagnostics( // (4,5): error CS0411: The type arguments for method 'Test1(T1, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // Test1(0, converter); + // Test1(string.Empty, converter); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(T1, delegate*)").WithLocation(4, 5), // (5,5): error CS0411: The type arguments for method 'Test2(T2, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // Test2(0, converter); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test2").WithArguments("Test2(T2, delegate*)").WithLocation(5, 5), - // (8,17): warning CS8321: The local function 'Test1' is declared but never used - // static void Test1(T1 t1, delegate* func) {} - Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "Test1").WithArguments("Test1").WithLocation(8, 17), - // (9,17): warning CS8321: The local function 'Test2' is declared but never used - // static void Test2(T2 t2, delegate* func) {} - Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "Test2").WithArguments("Test2").WithLocation(9, 17) + // (6,38): error CS8787: Cannot convert method group to function pointer (Are you missing a '&'?) + // Test1(string.Empty, converter); + Diagnostic(ErrorCode.ERR_MissingAddressOf, "converter").WithLocation(6, 38), + // (7,27): error CS8787: Cannot convert method group to function pointer (Are you missing a '&'?) + // Test2(0, converter); + Diagnostic(ErrorCode.ERR_MissingAddressOf, "converter").WithLocation(7, 27) + ); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_ThroughLambdaExpression() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe +{ + Test1(string.Empty, v => 0); + Test2(0, v => 0); + Test1(string.Empty, v => 0); + Test2(0, v => 0); + + static void Test1(T1 t1, delegate* func) {} + static void Test2(T2 t2, delegate* func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + comp.VerifyDiagnostics( + // (4,5): error CS0411: The type arguments for method 'Test1(T1, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test1(string.Empty, v => 0); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(T1, delegate*)").WithLocation(4, 5), + // (5,5): error CS0411: The type arguments for method 'Test2(T2, delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test2(0, v => 0); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test2").WithArguments("Test2(T2, delegate*)").WithLocation(5, 5), + // (6,38): error CS1660: Cannot convert lambda expression to type 'delegate*' because it is not a delegate type + // Test1(string.Empty, v => 0); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "v => 0").WithArguments("lambda expression", "delegate*").WithLocation(6, 38), + // (7,27): error CS1660: Cannot convert lambda expression to type 'delegate*' because it is not a delegate type + // Test2(0, v => 0); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "v => 0").WithArguments("lambda expression", "delegate*").WithLocation(7, 27) + ); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void AddressOfInference_OnDelegateType() + { + var comp = CreateCompilationWithFunctionPointers(@" +using System; +unsafe +{ + Test1(string.Empty, &converter); + Test2(0, &converter); + Test1(string.Empty, &converter); + Test2(0, &converter); + + static int converter(string v) => 0; + static void Test1(T1 t1, Func func) {} + static void Test2(T2 t2, Func func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + comp.VerifyDiagnostics( + // (5,5): error CS0411: The type arguments for method 'Test1(T1, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test1(string.Empty, &converter); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(T1, System.Func)").WithLocation(5, 5), + // (6,5): error CS0411: The type arguments for method 'Test2(T2, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test2(0, &converter); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test2").WithArguments("Test2(T2, System.Func)").WithLocation(6, 5), + // (7,38): error CS1503: Argument 2: cannot convert from '&method group' to 'Func' + // Test1(string.Empty, &converter); + Diagnostic(ErrorCode.ERR_BadArgType, "&converter").WithArguments("2", "&method group", "System.Func").WithLocation(7, 38), + // (8,27): error CS1503: Argument 2: cannot convert from '&method group' to 'Func' + // Test2(0, &converter); + Diagnostic(ErrorCode.ERR_BadArgType, "&converter").WithArguments("2", "&method group", "System.Func").WithLocation(8, 27) ); } From 56678c105fe54eca3f62da87f60483d26e3d0dc1 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 20 Jan 2021 17:38:23 -0800 Subject: [PATCH 07/12] Ensure that exact inference checks calling convention and refkind equality. --- .../OverloadResolution/MethodTypeInference.cs | 22 ++++++ .../Semantics/FunctionPointerTests.cs | 68 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 6c7ec34a7e9d..a234163a15e6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -1650,6 +1650,13 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio target.Type is FunctionPointerTypeSymbol { Signature: { ParameterCount: int targetParameterCount } targetSignature } && sourceParameterCount == targetParameterCount) { + if (sourceSignature.RefKind != targetSignature.RefKind + || sourceSignature.ParameterRefKinds != targetSignature.ParameterRefKinds + || !callingConventionsEqual(sourceSignature, targetSignature)) + { + return false; + } + for (int i = 0; i < sourceParameterCount; i++) { ExactInference(sourceSignature.ParameterTypesWithAnnotations[i], targetSignature.ParameterTypesWithAnnotations[i], ref useSiteDiagnostics); @@ -1660,6 +1667,21 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio } return false; + + static bool callingConventionsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) + { + if (sourceSignature.CallingConvention != targetSignature.CallingConvention) + { + return false; + } + + return (sourceSignature.GetCallingConventionModifiers(), targetSignature.GetCallingConventionModifiers()) switch + { + (null, null) => true, + ({ } sourceModifiers, { } targetModifiers) when sourceModifiers.SetEquals(targetModifiers) => true, + _ => false + }; + } } private void ExactTypeArgumentInference(NamedTypeSymbol source, NamedTypeSymbol target, ref HashSet useSiteDiagnostics) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index b6a14752d85f..3d4e6a1cbdb9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -2396,6 +2396,74 @@ static void Test2(T2 t2, Func func) {} ); } + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_ExactInferenceThroughArray_CallingConventionMismatch() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe +{ + delegate*[] ptr1 = null; + Test1(ptr1); + Test1(ptr1); + delegate* unmanaged[Cdecl, Stdcall][] ptr2 = null; + Test1(ptr2); + Test1(ptr2); + + static void Test1(delegate* unmanaged[] func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + comp.Assembly.SetOverrideRuntimeSupportsUnmanagedSignatureCallingConvention(); + + comp.VerifyDiagnostics( + // (5,5): error CS0411: The type arguments for method 'Test1(delegate* unmanaged[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test1(ptr1); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(delegate* unmanaged[])").WithLocation(5, 5), + // (6,19): error CS1503: Argument 1: cannot convert from 'delegate*[]' to 'delegate* unmanaged[]' + // Test1(ptr1); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr1").WithArguments("1", "delegate*[]", "delegate* unmanaged[]").WithLocation(6, 19), + // (8,5): error CS0411: The type arguments for method 'Test1(delegate* unmanaged[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test1(ptr2); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(delegate* unmanaged[])").WithLocation(8, 5), + // (9,19): error CS1503: Argument 1: cannot convert from 'delegate* unmanaged[Cdecl, Stdcall][]' to 'delegate* unmanaged[]' + // Test1(ptr2); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr2").WithArguments("1", "delegate* unmanaged[Cdecl, Stdcall][]", "delegate* unmanaged[]").WithLocation(9, 19) + ); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_ExactInferenceThroughArray_RefKindMismatch() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe +{ + delegate*[] ptr1 = null; + Test1(ptr1); + Test1(ptr1); + delegate*[] ptr2 = null; + Test1(ptr2); + Test1(ptr2); + + static void Test1(delegate*[] func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + comp.VerifyDiagnostics( + // (5,5): error CS0411: The type arguments for method 'Test1(delegate*[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test1(ptr1); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(delegate*[])").WithLocation(5, 5), + // (6,27): error CS1503: Argument 1: cannot convert from 'delegate*[]' to 'delegate*[]' + // Test1(ptr1); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr1").WithArguments("1", "delegate*[]", "delegate*[]").WithLocation(6, 27), + // (8,5): error CS0411: The type arguments for method 'Test1(delegate*[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test1(ptr2); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test1").WithArguments("Test1(delegate*[])").WithLocation(8, 5), + // (9,27): error CS1503: Argument 1: cannot convert from 'delegate*[]' to 'delegate*[]' + // Test1(ptr2); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr2").WithArguments("1", "delegate*[]", "delegate*[]").WithLocation(9, 27) + ); + } + [Fact] public void FunctionPointerTypeCannotBeUsedInDynamicTypeArguments() { From 19b2492d8064d2b38a8a4f8ad7a511c5af3732b9 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 25 Jan 2021 10:32:17 -0800 Subject: [PATCH 08/12] Fix bug with parameter ref kind comparison, add test to cover. --- .../OverloadResolution/MethodTypeInference.cs | 12 +++++-- .../Semantics/FunctionPointerTests.cs | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index a234163a15e6..adbd81280d5e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -1650,9 +1650,7 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio target.Type is FunctionPointerTypeSymbol { Signature: { ParameterCount: int targetParameterCount } targetSignature } && sourceParameterCount == targetParameterCount) { - if (sourceSignature.RefKind != targetSignature.RefKind - || sourceSignature.ParameterRefKinds != targetSignature.ParameterRefKinds - || !callingConventionsEqual(sourceSignature, targetSignature)) + if (!refKindsEqual(sourceSignature, targetSignature) || !callingConventionsEqual(sourceSignature, targetSignature)) { return false; } @@ -1668,6 +1666,14 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio return false; + static bool refKindsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) + { + return sourceSignature.RefKind == targetSignature.RefKind + && sourceSignature.ParameterRefKinds.IsDefault + ? targetSignature.ParameterRefKinds.IsDefault + : sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds); + } + static bool callingConventionsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) { if (sourceSignature.CallingConvention != targetSignature.CallingConvention) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index 3d4e6a1cbdb9..5bb25bc6ebc0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -2464,6 +2464,40 @@ static void Test1(delegate*[] func) {} ); } + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_ExactInferenceThroughArray_RefKindMatch() + { + var verifier = CompileAndVerify(@" +unsafe +{ + var ptr1 = new delegate*[] { &Test }; + Test1(ptr1); + + static string Test(ref string s) => s = ""1""; + static void Test1(delegate*[] func) { + T1 t = default; + System.Console.Write(func[0](ref t)); + System.Console.Write(t); + } +} +", expectedOutput: "11", options: TestOptions.UnsafeReleaseExe, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped); + + verifier.VerifyIL("", @" +{ + // Code size 21 (0x15) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: newarr ""delegate*"" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldftn ""string $.<
$>g__Test|0_0(ref string)"" + IL_000e: stelem.i + IL_000f: call ""void $.<
$>g__Test1|0_1(delegate*[])"" + IL_0014: ret +} +"); + } + [Fact] public void FunctionPointerTypeCannotBeUsedInDynamicTypeArguments() { From 31759393c221de3979681e82db98c747c9a94a68 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 25 Jan 2021 11:18:21 -0800 Subject: [PATCH 09/12] Fix potential null ref. --- .../OverloadResolution/MethodTypeInference.cs | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index adbd81280d5e..690e73d692a0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -1650,7 +1650,7 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio target.Type is FunctionPointerTypeSymbol { Signature: { ParameterCount: int targetParameterCount } targetSignature } && sourceParameterCount == targetParameterCount) { - if (!refKindsEqual(sourceSignature, targetSignature) || !callingConventionsEqual(sourceSignature, targetSignature)) + if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature) || !callingConventionsEqual(sourceSignature, targetSignature)) { return false; } @@ -1666,14 +1666,6 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio return false; - static bool refKindsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) - { - return sourceSignature.RefKind == targetSignature.RefKind - && sourceSignature.ParameterRefKinds.IsDefault - ? targetSignature.ParameterRefKinds.IsDefault - : sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds); - } - static bool callingConventionsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) { if (sourceSignature.CallingConvention != targetSignature.CallingConvention) @@ -1690,6 +1682,17 @@ static bool callingConventionsEqual(FunctionPointerMethodSymbol sourceSignature, } } + private static bool FunctionPointerRefKindsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) + { + return sourceSignature.RefKind == targetSignature.RefKind + && (sourceSignature.ParameterRefKinds.IsDefault, targetSignature.ParameterRefKinds.IsDefault) switch + { + (true, true) => true, + (true, _) or (_, true) => false, + _ => sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds) + }; + } + private void ExactTypeArgumentInference(NamedTypeSymbol source, NamedTypeSymbol target, ref HashSet useSiteDiagnostics) { Debug.Assert((object)source != null); @@ -2142,14 +2145,7 @@ private bool LowerBoundFunctionPointerTypeInference(TypeSymbol source, TypeSymbo return false; } - if (sourceSignature.RefKind != targetSignature.RefKind) - { - return false; - } - - if (sourceSignature.ParameterRefKinds.IsDefault - ? !targetSignature.ParameterRefKinds.IsDefault - : !sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds)) + if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature)) { return false; } @@ -2499,14 +2495,7 @@ private bool UpperBoundFunctionPointerTypeInference(TypeSymbol source, TypeSymbo return false; } - if (sourceSignature.RefKind != targetSignature.RefKind) - { - return false; - } - - if (sourceSignature.ParameterRefKinds.IsDefault - ? !targetSignature.ParameterRefKinds.IsDefault - : !sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds)) + if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature)) { return false; } From 532079e3bf0f6f0eb1f3a908915b0388a52da644 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 25 Jan 2021 12:04:50 -0800 Subject: [PATCH 10/12] Small formatting change for clarity. --- .../Binder/Semantics/OverloadResolution/MethodTypeInference.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 690e73d692a0..2dc65247ccdf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -1687,8 +1687,8 @@ private static bool FunctionPointerRefKindsEqual(FunctionPointerMethodSymbol sou return sourceSignature.RefKind == targetSignature.RefKind && (sourceSignature.ParameterRefKinds.IsDefault, targetSignature.ParameterRefKinds.IsDefault) switch { + (true, false) or (false, true) => false, (true, true) => true, - (true, _) or (_, true) => false, _ => sourceSignature.ParameterRefKinds.SequenceEqual(targetSignature.ParameterRefKinds) }; } From 0bc23650c9b9ee97c13aaed3db2fa85a2375d263 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 25 Jan 2021 12:54:08 -0800 Subject: [PATCH 11/12] Check calling convention equality during type inference first phase in all cases. --- .../OverloadResolution/MethodTypeInference.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 2dc65247ccdf..5d364a732dc0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -1650,7 +1650,7 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio target.Type is FunctionPointerTypeSymbol { Signature: { ParameterCount: int targetParameterCount } targetSignature } && sourceParameterCount == targetParameterCount) { - if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature) || !callingConventionsEqual(sourceSignature, targetSignature)) + if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature) || !FunctionPointerCallingConventionsEqual(sourceSignature, targetSignature)) { return false; } @@ -1665,21 +1665,21 @@ private bool ExactPointerInference(TypeWithAnnotations source, TypeWithAnnotatio } return false; + } - static bool callingConventionsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) + private static bool FunctionPointerCallingConventionsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) + { + if (sourceSignature.CallingConvention != targetSignature.CallingConvention) { - if (sourceSignature.CallingConvention != targetSignature.CallingConvention) - { - return false; - } - - return (sourceSignature.GetCallingConventionModifiers(), targetSignature.GetCallingConventionModifiers()) switch - { - (null, null) => true, - ({ } sourceModifiers, { } targetModifiers) when sourceModifiers.SetEquals(targetModifiers) => true, - _ => false - }; + return false; } + + return (sourceSignature.GetCallingConventionModifiers(), targetSignature.GetCallingConventionModifiers()) switch + { + (null, null) => true, + ({ } sourceModifiers, { } targetModifiers) when sourceModifiers.SetEquals(targetModifiers) => true, + _ => false + }; } private static bool FunctionPointerRefKindsEqual(FunctionPointerMethodSymbol sourceSignature, FunctionPointerMethodSymbol targetSignature) @@ -2145,7 +2145,7 @@ private bool LowerBoundFunctionPointerTypeInference(TypeSymbol source, TypeSymbo return false; } - if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature)) + if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature) || !FunctionPointerCallingConventionsEqual(sourceSignature, targetSignature)) { return false; } @@ -2495,7 +2495,7 @@ private bool UpperBoundFunctionPointerTypeInference(TypeSymbol source, TypeSymbo return false; } - if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature)) + if (!FunctionPointerRefKindsEqual(sourceSignature, targetSignature) || !FunctionPointerCallingConventionsEqual(sourceSignature, targetSignature)) { return false; } From 415955db3c4658a7c5cdcb5c4aaf777c2cca9c0f Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 25 Jan 2021 13:12:47 -0800 Subject: [PATCH 12/12] Add more testing. --- .../Semantics/FunctionPointerTests.cs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index 5bb25bc6ebc0..8d745694d839 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -2498,6 +2498,120 @@ .maxstack 4 "); } + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_LowerBoundInference_CallingConventionMismatch() + { + var verifier = CreateCompilationWithFunctionPointers(@" +unsafe +{ + delegate* ptr = null; + Test(ptr); + Test(ptr); + + static void Test(delegate* unmanaged[Cdecl] func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + verifier.VerifyDiagnostics( + // (5,5): error CS0411: The type arguments for method 'Test(delegate* unmanaged[Cdecl])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test(ptr); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Test(delegate* unmanaged[Cdecl])").WithLocation(5, 5), + // (6,23): error CS1503: Argument 1: cannot convert from 'delegate*' to 'delegate* unmanaged[Cdecl]' + // Test(ptr); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr").WithArguments("1", "delegate*", "delegate* unmanaged[Cdecl]").WithLocation(6, 23) + ); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_LowerBoundInference_RefKindMismatch() + { + var verifier = CreateCompilationWithFunctionPointers(@" +unsafe +{ + delegate* ptr1 = null; + Test(ptr1); + Test(ptr1); + delegate* ptr2 = null; + Test(ptr2); + Test(ptr2); + + static void Test(delegate* func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + verifier.VerifyDiagnostics( + // (5,5): error CS0411: The type arguments for method 'Test(delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test(ptr1); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Test(delegate*)").WithLocation(5, 5), + // (6,26): error CS1503: Argument 1: cannot convert from 'delegate*' to 'delegate*' + // Test(ptr1); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr1").WithArguments("1", "delegate*", "delegate*").WithLocation(6, 26), + // (8,5): error CS0411: The type arguments for method 'Test(delegate*)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test(ptr2); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Test(delegate*)").WithLocation(8, 5), + // (9,26): error CS1503: Argument 1: cannot convert from 'delegate*' to 'delegate*' + // Test(ptr2); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr2").WithArguments("1", "delegate*", "delegate*").WithLocation(9, 26) + ); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_UpperBoundInference_CallingConventionMismatch() + { + var verifier = CreateCompilationWithFunctionPointers(@" +unsafe +{ + delegate*, void> ptr = null; + Test(ptr); + Test(ptr); + + static void Test(delegate*, void> func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + verifier.VerifyDiagnostics( + // (5,5): error CS0411: The type arguments for method 'Test(delegate*, void>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test(ptr); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Test(delegate*, void>)").WithLocation(5, 5), + // (6,26): error CS1503: Argument 1: cannot convert from 'delegate*, void>' to 'delegate*, void>' + // Test(ptr); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr").WithArguments("1", "delegate*, void>", "delegate*, void>").WithLocation(6, 26) + ); + } + + [Fact, WorkItem(50096, "https://github.com/dotnet/roslyn/issues/50096")] + public void FunctionPointerInference_UpperBoundInference_RefKindMismatch() + { + var verifier = CreateCompilationWithFunctionPointers(@" +unsafe +{ + delegate*, void> ptr1 = null; + Test(ptr1); + Test(ptr1); + delegate*, void> ptr2 = null; + Test(ptr2); + Test(ptr2); + + static void Test(delegate*, void> func) {} +} +", options: TestOptions.UnsafeReleaseExe); + + verifier.VerifyDiagnostics( + // (5,5): error CS0411: The type arguments for method 'Test(delegate*, void>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test(ptr1); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Test(delegate*, void>)").WithLocation(5, 5), + // (6,26): error CS1503: Argument 1: cannot convert from 'delegate*, void>' to 'delegate*, void>' + // Test(ptr1); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr1").WithArguments("1", "delegate*, void>", "delegate*, void>").WithLocation(6, 26), + // (8,5): error CS0411: The type arguments for method 'Test(delegate*, void>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test(ptr2); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Test(delegate*, void>)").WithLocation(8, 5), + // (9,26): error CS1503: Argument 1: cannot convert from 'delegate*, void>' to 'delegate*, void>' + // Test(ptr2); + Diagnostic(ErrorCode.ERR_BadArgType, "ptr2").WithArguments("1", "delegate*, void>", "delegate*, void>").WithLocation(9, 26) + ); + } + [Fact] public void FunctionPointerTypeCannotBeUsedInDynamicTypeArguments() {