From 6b6689f71fa545c1191c478ac71d57b51b7c7bf2 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 6 May 2020 16:48:58 -0700 Subject: [PATCH 01/11] Add public API access check support. --- .../Semantic/Semantics/AccessCheckTests.cs | 24 +++++++++++++++++ .../Core/Portable/Compilation/Compilation.cs | 27 ++++++++++++++----- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs index 81a24666c1639..76ba983c416ca 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs @@ -709,6 +709,10 @@ private class K {} private A[] aarray; private K* kptr; private A* aptr; + private delegate* kinreturnfuncptr; + private delegate* kinparamfuncptr1; + private delegate* kinparamfuncptr2; + private delegate* afuncptr; private IEnumerable kenum; private IEnumerable aenum; void M() @@ -746,6 +750,10 @@ class ADerived2: A ITypeSymbol aarrayType = (classA.GetMembers("aarray").Single() as IFieldSymbol).Type; ITypeSymbol kptrType = (classA.GetMembers("kptr").Single() as IFieldSymbol).Type; ITypeSymbol aptrType = (classA.GetMembers("aptr").Single() as IFieldSymbol).Type; + ITypeSymbol kinreturnfuncptrType = (classA.GetMembers("kinreturnfuncptr").Single() as IFieldSymbol).Type; + ITypeSymbol kinparamfuncptr1Type = (classA.GetMembers("kinparamfuncptr1").Single() as IFieldSymbol).Type; + ITypeSymbol kinparamfuncptr2Type = (classA.GetMembers("kinparamfuncptr2").Single() as IFieldSymbol).Type; + ITypeSymbol afuncptrType = (classA.GetMembers("afuncptr").Single() as IFieldSymbol).Type; ITypeSymbol kenumType = (classA.GetMembers("kenum").Single() as IFieldSymbol).Type; ITypeSymbol aenumType = (classA.GetMembers("aenum").Single() as IFieldSymbol).Type; var discards = tree.GetRoot().DescendantNodes().OfType().Where(i => i.Identifier.ContextualKind() == SyntaxKind.UnderscoreToken).ToArray(); @@ -774,6 +782,14 @@ class ADerived2: A Assert.False(compilation.IsSymbolAccessibleWithin(kptrType, classB)); Assert.True(Symbol.IsSymbolAccessible(aptrType.GetSymbol(), classB.GetSymbol())); Assert.True(compilation.IsSymbolAccessibleWithin(aptrType, classB)); + Assert.False(Symbol.IsSymbolAccessible(kinreturnfuncptrType.GetSymbol(), classB.GetSymbol())); + Assert.False(compilation.IsSymbolAccessibleWithin(kinreturnfuncptrType, classB)); + Assert.False(Symbol.IsSymbolAccessible(kinparamfuncptr1Type.GetSymbol(), classB.GetSymbol())); + Assert.False(compilation.IsSymbolAccessibleWithin(kinparamfuncptr1Type, classB)); + Assert.False(Symbol.IsSymbolAccessible(kinparamfuncptr2Type.GetSymbol(), classB.GetSymbol())); + Assert.False(compilation.IsSymbolAccessibleWithin(kinparamfuncptr2Type, classB)); + Assert.True(Symbol.IsSymbolAccessible(afuncptrType.GetSymbol(), classB.GetSymbol())); + Assert.True(compilation.IsSymbolAccessibleWithin(afuncptrType, classB)); Assert.False(Symbol.IsSymbolAccessible(kdiscard.GetSymbol(), classB.GetSymbol())); Assert.False(compilation.IsSymbolAccessibleWithin(kdiscard, classB)); Assert.True(Symbol.IsSymbolAccessible(adiscard.GetSymbol(), classB.GetSymbol())); @@ -811,8 +827,16 @@ class ADerived2: A Assert.False(compilation.IsSymbolAccessibleWithin(karrayType, sourceAssem)); Assert.True(Symbol.IsSymbolAccessible(aptrType.GetSymbol(), sourceAssem.GetSymbol())); Assert.True(compilation.IsSymbolAccessibleWithin(aptrType, sourceAssem)); + Assert.True(Symbol.IsSymbolAccessible(afuncptrType.GetSymbol(), sourceAssem.GetSymbol())); + Assert.True(compilation.IsSymbolAccessibleWithin(afuncptrType, sourceAssem)); Assert.False(Symbol.IsSymbolAccessible(kptrType.GetSymbol(), sourceAssem.GetSymbol())); Assert.False(compilation.IsSymbolAccessibleWithin(kptrType, sourceAssem)); + Assert.False(Symbol.IsSymbolAccessible(kinreturnfuncptrType.GetSymbol(), sourceAssem.GetSymbol())); + Assert.False(compilation.IsSymbolAccessibleWithin(kinreturnfuncptrType, sourceAssem)); + Assert.False(Symbol.IsSymbolAccessible(kinparamfuncptr1Type.GetSymbol(), sourceAssem.GetSymbol())); + Assert.False(compilation.IsSymbolAccessibleWithin(kinparamfuncptr1Type, sourceAssem)); + Assert.False(Symbol.IsSymbolAccessible(kinparamfuncptr2Type.GetSymbol(), sourceAssem.GetSymbol())); + Assert.False(compilation.IsSymbolAccessibleWithin(kinparamfuncptr2Type, sourceAssem)); Assert.True(Symbol.IsSymbolAccessible(adiscard.GetSymbol(), sourceAssem.GetSymbol())); Assert.True(compilation.IsSymbolAccessibleWithin(adiscard, sourceAssem)); Assert.False(Symbol.IsSymbolAccessible(kdiscard.GetSymbol(), sourceAssem.GetSymbol())); diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index c8870984cc6a8..3487971b7f3c2 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1295,8 +1295,7 @@ public bool IsSymbolAccessibleWithin( void checkInCompilationReferences(ISymbol s, string parameterName) { - var containingAssembly = computeContainingAssembly(s); - if (!assemblyIsInReferences(containingAssembly)) + if (!isContainingAssemblyInReferences(s)) { throw new ArgumentException(string.Format(CodeAnalysisResources.IsSymbolAccessibleWrongAssembly, parameterName), parameterName); } @@ -1344,14 +1343,14 @@ bool assemblyIsInCompilationReferences(IAssemblySymbol a, Compilation compilatio return false; } - IAssemblySymbol computeContainingAssembly(ISymbol s) + bool isContainingAssemblyInReferences(ISymbol s) { while (true) { switch (s.Kind) { case SymbolKind.Assembly: - return (IAssemblySymbol)s; + return assemblyIsInReferences((IAssemblySymbol)s); case SymbolKind.PointerType: s = ((IPointerTypeSymbol)s).PointedAtType; continue; @@ -1364,15 +1363,31 @@ IAssemblySymbol computeContainingAssembly(ISymbol s) case SymbolKind.Discard: s = ((IDiscardSymbol)s).Type; continue; + case SymbolKind.FunctionPointer: + var funcPtr = (IFunctionPointerTypeSymbol)s; + if (!isContainingAssemblyInReferences(funcPtr.Signature.ReturnType)) + { + return false; + } + + foreach (var param in funcPtr.Signature.Parameters) + { + if (!isContainingAssemblyInReferences(param.Type)) + { + return false; + } + } + + return true; case SymbolKind.DynamicType: case SymbolKind.ErrorType: case SymbolKind.Preprocessing: case SymbolKind.Namespace: // these symbols are not restricted in where they can be accessed, so unless they report // a containing assembly, we treat them as in the current assembly for access purposes - return s.ContainingAssembly ?? this.Assembly; + return assemblyIsInReferences(s.ContainingAssembly ?? this.Assembly); default: - return s.ContainingAssembly; + return assemblyIsInReferences(s.ContainingAssembly); } } } From cae7f81659369762011683f094d645c916d1a1b8 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 7 May 2020 10:49:56 -0700 Subject: [PATCH 02/11] Function pointer symbols cannot be referenced by name. --- src/Compilers/CSharp/Portable/Symbols/Symbol.cs | 1 + src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 992aa43bfa3c4..2a56cf9d889b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -512,6 +512,7 @@ public bool CanBeReferencedByName case SymbolKind.ArrayType: case SymbolKind.PointerType: + case SymbolKind.FunctionPointer: case SymbolKind.Assembly: case SymbolKind.DynamicType: case SymbolKind.NetModule: diff --git a/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs b/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs index 1f1238ed66b40..8b981826095b8 100644 --- a/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs +++ b/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs @@ -32,6 +32,7 @@ static void verifyPointerType(FunctionPointerTypeSymbol symbol) Assert.False(symbol.IsStatic); Assert.False(symbol.IsAbstract); Assert.False(symbol.IsSealed); + Assert.False(symbol.CanBeReferencedByName); Assert.True(symbol.IsValueType); Assert.True(symbol.CanBeAssignedNull()); From 8c9f3267756110a1ad3c607769323ad302ddc9b6 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 7 May 2020 11:31:53 -0700 Subject: [PATCH 03/11] A PointerType's sort order is never usable today. --- src/Compilers/Core/Portable/Symbols/SymbolKindExtensions.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Compilers/Core/Portable/Symbols/SymbolKindExtensions.cs b/src/Compilers/Core/Portable/Symbols/SymbolKindExtensions.cs index 46f110ed83b43..ccda7721dc4e7 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolKindExtensions.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolKindExtensions.cs @@ -48,12 +48,10 @@ public static int ToSortOrder(this SymbolKind kind) return 14; case SymbolKind.TypeParameter: return 15; - case SymbolKind.PointerType: - return 16; case SymbolKind.DynamicType: - return 17; + return 16; case SymbolKind.Preprocessing: - return 18; + return 17; default: throw ExceptionUtilities.UnexpectedValue(kind); } From 5d5dcc539d496e85ae1758f919904c1a90be678f Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 8 May 2020 10:50:44 -0700 Subject: [PATCH 04/11] Return true for IsTypeOrTypeAlias for function pointers. --- src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs | 1 + src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs index c6df22da2a9ad..3dfbb320818e9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs @@ -250,6 +250,7 @@ public static bool IsTypeOrTypeAlias(this Symbol symbol) case SymbolKind.ErrorType: case SymbolKind.NamedType: case SymbolKind.PointerType: + case SymbolKind.FunctionPointer: case SymbolKind.TypeParameter: return true; case SymbolKind.Alias: diff --git a/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs b/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs index 8b981826095b8..6213df698787d 100644 --- a/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs +++ b/src/Compilers/Test/Utilities/CSharp/FunctionPointerUtilities.cs @@ -33,6 +33,7 @@ static void verifyPointerType(FunctionPointerTypeSymbol symbol) Assert.False(symbol.IsAbstract); Assert.False(symbol.IsSealed); Assert.False(symbol.CanBeReferencedByName); + Assert.True(symbol.IsTypeOrTypeAlias()); Assert.True(symbol.IsValueType); Assert.True(symbol.CanBeAssignedNull()); From 3f94e67ed3d860959ff12fb305445606be9bc96e Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 8 May 2020 11:28:46 -0700 Subject: [PATCH 05/11] Support normalizing task-like types in function pointer types. --- .../Portable/Symbols/TypeSymbolExtensions.cs | 48 ++++++++++ .../Semantics/OverloadResolutionTests.cs | 88 +++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 94cade87fbeef..c885340145a29 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1628,6 +1628,13 @@ private static bool NormalizeTaskTypesInType(CSharpCompilation compilation, ref type = pointerType; return changed; } + case SymbolKind.FunctionPointer: + { + var functionPointerType = (FunctionPointerTypeSymbol)type; + var changed = NormalizeTaskTypesInFunctionPointer(compilation, ref functionPointerType); + type = functionPointerType; + return changed; + } } return false; } @@ -1722,6 +1729,47 @@ private static bool NormalizeTaskTypesInPointer(CSharpCompilation compilation, r return true; } + private static bool NormalizeTaskTypesInFunctionPointer(CSharpCompilation compilation, ref FunctionPointerTypeSymbol funcPtrType) + { + var returnType = funcPtrType.Signature.ReturnTypeWithAnnotations; + var madeChanges = NormalizeTaskTypesInType(compilation, ref returnType); + + var paramTypes = ImmutableArray.Empty; + + if (funcPtrType.Signature.ParameterCount > 0) + { + var paramsBuilder = ArrayBuilder.GetInstance(funcPtrType.Signature.ParameterCount); + bool madeParamChanges = false; + foreach (var param in funcPtrType.Signature.Parameters) + { + var paramType = param.TypeWithAnnotations; + madeParamChanges |= NormalizeTaskTypesInType(compilation, ref paramType); + paramsBuilder.Add(paramType); + } + + if (madeParamChanges) + { + madeChanges = true; + paramTypes = paramsBuilder.ToImmutableAndFree(); + } + else + { + paramTypes = funcPtrType.Signature.ParameterTypesWithAnnotations; + paramsBuilder.Free(); + } + } + + if (madeChanges) + { + funcPtrType = funcPtrType.SubstituteTypeSymbol(returnType, paramTypes, refCustomModifiers: default, paramRefCustomModifiers: default); + return true; + } + else + { + return false; + } + } + internal static Cci.TypeReferenceWithAttributes GetTypeRefWithAttributes( this TypeWithAnnotations type, Emit.PEModuleBuilder moduleBuilder, diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs index 1b2485ec5699e..044947e69e1ff 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs @@ -843,6 +843,94 @@ .method public hidebysig specialname rtspecialname instance void .ctor(class [ms Assert.Equal("System.Threading.Tasks.Task modopt(MyTask) *[]", normalized.ToTestDisplayString()); } + [Fact] + public void NormalizeTaskTypes_FunctionPointers() + { + + string source = +@" +using System.Runtime.CompilerServices; +unsafe class C +{ +#pragma warning disable CS0169 + static delegate*>> F0; + static delegate*>, int, int> F1; + static delegate*>, int> F2; + static delegate* F3; +#pragma warning restore CS0169 +} +[AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))] +struct MyTask { } +struct MyTaskMethodBuilder +{ + public static MyTaskMethodBuilder Create() => new MyTaskMethodBuilder(); +} + +namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type t) { } } } +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview); + compilation.VerifyDiagnostics(); + + assert("F0", "delegate*>>", "delegate*>>"); + assert("F1", "delegate*>, System.Int32, System.Int32>", "delegate*>, System.Int32, System.Int32>"); + assert("F2", "delegate*>, System.Int32>", "delegate*>, System.Int32>"); + assert("F3", "delegate*", normalized: null); + + void assert(string fieldName, string original, string normalized) + { + var type = compilation.GetMember($"C.{fieldName}").Type; + FunctionPointerUtilities.CommonVerifyFunctionPointer((FunctionPointerTypeSymbol)type); + var normalizedType = type.NormalizeTaskTypes(compilation); + Assert.Equal(original, type.ToTestDisplayString()); + if (normalized is object) + { + Assert.Equal(normalized, normalizedType.ToTestDisplayString()); + } + else + { + Assert.Same(type, normalizedType); + } + } + } + + [Fact] + public void NormalizeTaskTypes_FunctionPointersCustomModifiers() + { + var ilSource = +@".class public C +{ + .field public static method class MyTask modopt(class MyTask) *(class MyTask modopt(class MyTask)) F0 + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class public MyTask +{ + .custom instance void System.Runtime.CompilerServices.AsyncMethodBuilderAttribute::.ctor(class [mscorlib]System.Type) = { type(MyTaskMethodBuilder) } + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class public MyTaskMethodBuilder +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.namespace System.Runtime.CompilerServices +{ + .class public AsyncMethodBuilderAttribute extends [mscorlib]System.Attribute + { + .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Type t) cil managed { ret } + } +} +"; + var source = +@""; + var reference = CompileIL(ilSource); + var compilation = CreateCompilationWithMscorlib45(source, references: new[] { reference }); + compilation.VerifyDiagnostics(); + + var type = compilation.GetMember("C.F0").Type; + var normalized = type.NormalizeTaskTypes(compilation); + Assert.Equal("delegate*", type.ToTestDisplayString()); + Assert.Equal("delegate*", normalized.ToTestDisplayString()); + } + [Fact] public void NormalizeTaskTypes_Errors() { From 447d87f6a0f43786ac15d53f3d0ff20a8c53f75f Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 13 May 2020 13:07:46 -0700 Subject: [PATCH 06/11] Support for overloading and overriding signatures with function pointers. --- .../Semantics/Conversions/ConversionsBase.cs | 16 +- .../FunctionPointerMethodSymbol.cs | 2 +- .../FunctionPointerParameterSymbol.cs | 2 +- .../FunctionPointerTypeSymbol.cs | 17 + .../Symbols/MemberSignatureComparer.cs | 6 +- .../Symbols/Source/CustomModifierUtils.cs | 3 +- .../CodeGen/CodeGenFunctionPointersTests.cs | 909 ++++++++++++++++++ .../Semantics/FunctionPointerTests.cs | 9 +- .../Core/Portable/Symbols/TypeCompareKind.cs | 1 + 9 files changed, 952 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 51649bb901a13..6739073b8f0b1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -2966,26 +2966,28 @@ internal bool HasImplicitPointerConversion(TypeSymbol? source, TypeSymbol? desti return false; } - if (!hasConversion(sourceParam.RefKind, destinationSig.Parameters[i].Type, sourceSig.Parameters[i].Type, ref useSiteDiagnostics)) + if (!hasConversion(sourceParam.RefKind, destinationSig.Parameters[i].TypeWithAnnotations, sourceSig.Parameters[i].TypeWithAnnotations, ref useSiteDiagnostics)) { return false; } } return sourceSig.RefKind == destinationSig.RefKind - && hasConversion(sourceSig.RefKind, sourceSig.ReturnType, destinationSig.ReturnType, ref useSiteDiagnostics); + && hasConversion(sourceSig.RefKind, sourceSig.ReturnTypeWithAnnotations, destinationSig.ReturnTypeWithAnnotations, ref useSiteDiagnostics); - bool hasConversion(RefKind refKind, TypeSymbol sourceType, TypeSymbol destinationType, ref HashSet? useSiteDiagnostics) + bool hasConversion(RefKind refKind, TypeWithAnnotations sourceType, TypeWithAnnotations destinationType, ref HashSet? useSiteDiagnostics) { switch (refKind) { case RefKind.None: - return HasIdentityOrImplicitReferenceConversion(sourceType, destinationType, ref useSiteDiagnostics) - || HasImplicitPointerToVoidConversion(sourceType, destinationType) - || HasImplicitPointerConversion(sourceType, destinationType, ref useSiteDiagnostics); + return (!IncludeNullability || HasTopLevelNullabilityImplicitConversion(sourceType, destinationType)) + && (HasIdentityOrImplicitReferenceConversion(sourceType.Type, destinationType.Type, ref useSiteDiagnostics) + || HasImplicitPointerToVoidConversion(sourceType.Type, destinationType.Type) + || HasImplicitPointerConversion(sourceType.Type, destinationType.Type, ref useSiteDiagnostics)); default: - return HasIdentityConversion(sourceType, destinationType); + return (!IncludeNullability || HasTopLevelNullabilityIdentityConversion(sourceType, destinationType)) + && HasIdentityConversion(sourceType.Type, destinationType.Type); } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 03a326d93a5af..3b15f07b4ebb8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -381,7 +381,7 @@ internal bool Equals(FunctionPointerMethodSymbol other, TypeCompareKind compareK private bool EqualsNoParameters(FunctionPointerMethodSymbol other, TypeCompareKind compareKind, IReadOnlyDictionary? isValueTypeOverride) => CallingConvention == other.CallingConvention - && RefKind == other.RefKind + && FunctionPointerTypeSymbol.RefKindEquals(compareKind, RefKind, other.RefKind) && ((compareKind & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) != 0 || RefCustomModifiers.SequenceEqual(other.RefCustomModifiers)) && ReturnTypeWithAnnotations.Equals(other.ReturnTypeWithAnnotations, compareKind, isValueTypeOverride); diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs index be7d3975caada..98cf57e20a655 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs @@ -49,7 +49,7 @@ internal bool Equals(FunctionPointerParameterSymbol other, TypeCompareKind compa && _containingSymbol.Equals(other._containingSymbol, compareKind, isValueTypeOverride); internal bool MethodEqualityChecks(FunctionPointerParameterSymbol other, TypeCompareKind compareKind, IReadOnlyDictionary? isValueTypeOverride) - => RefKind == other.RefKind + => FunctionPointerTypeSymbol.RefKindEquals(compareKind, RefKind, other.RefKind) && ((compareKind & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) != 0 || RefCustomModifiers.SequenceEqual(other.RefCustomModifiers)) && TypeWithAnnotations.Equals(other.TypeWithAnnotations, compareKind, isValueTypeOverride); diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs index 0f3318a5af192..83c1d49984bbb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs @@ -157,5 +157,22 @@ internal override TypeSymbol SetNullabilityForReferenceTypes(Func true, + (RefKind.None, _) => false, + (_, RefKind.None) => false, + _ => true + }; + } + else + { + return refKind1 == refKind2; + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index 5b82cad123ce8..21af5570fe7a2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -99,7 +99,7 @@ internal class MemberSignatureComparer : IEqualityComparer considerTypeConstraints: false, considerCallingConvention: false, considerRefKindDifferences: false, - typeComparison: TypeCompareKind.AllIgnoreOptions); //shouldn't actually matter for source members + typeComparison: TypeCompareKind.AllIgnoreOptions); /// /// This instance is used to determine if a partial method implementation matches the definition. @@ -313,6 +313,10 @@ private MemberSignatureComparer( _considerCallingConvention = considerCallingConvention; _considerRefKindDifferences = considerRefKindDifferences; _typeComparison = typeComparison; + if (!considerRefKindDifferences) + { + _typeComparison |= TypeCompareKind.FunctionPointerRefMatchesOutInRefReadonly; + } } #region IEqualityComparer Members diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs index 8e1558af7e33b..424420c67847e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs @@ -102,7 +102,8 @@ internal static TypeSymbol CopyTypeCustomModifiers(TypeSymbol sourceType, TypeSy Debug.Assert(resultType.Equals(sourceType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); // Same custom modifiers as source type. - Debug.Assert(resultType.Equals(destinationType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)); // Same object/dynamic, nullability and tuple names as destination type. + // Same object/dynamic, nullability and tuple names as destination type. + Debug.Assert(resultType.Equals(destinationType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNativeIntegers)); return resultType; } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index d015a7a0ef85e..5c46630c97c2d 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -5571,6 +5571,915 @@ .locals init (delegate* V_0, //ptr1 "); } + [Fact] + public void Overloading_ReturnTypes() + { + var verifier = CompileAndVerifyFunctionPointers(@" +using System; +unsafe class C +{ + static void M(delegate* ptr) => ptr(); + static void M(delegate* ptr) => Console.WriteLine(ptr()); + static void M(delegate* ptr) => Console.WriteLine(ptr()); + static void Ptr_Void() => Console.WriteLine(""Void""); + static object Ptr_Obj() => ""Object""; + static string Ptr_Str() => ""String""; + + static void Main() + { + M(&Ptr_Void); + M(&Ptr_Obj); + M(&Ptr_Str); + } +} +", expectedOutput: @" +Void +Object +String"); + + verifier.VerifyIL("C.Main", @" +{ + // Code size 34 (0x22) + .maxstack 1 + IL_0000: ldftn ""void C.Ptr_Void()"" + IL_0006: call ""void C.M(delegate*)"" + IL_000b: ldftn ""object C.Ptr_Obj()"" + IL_0011: call ""void C.M(delegate*)"" + IL_0016: ldftn ""string C.Ptr_Str()"" + IL_001c: call ""void C.M(delegate*)"" + IL_0021: ret +} +"); + } + + [Fact] + public void Overloading_ValidReturnRefness() + { + var verifier = CompileAndVerifyFunctionPointers(@" +using System; +unsafe class C +{ + static object field = ""R""; + static void M(delegate* ptr) => Console.WriteLine(ptr()); + static void M(delegate* ptr) + { + Console.Write(field); + ref var local = ref ptr(); + local = ""ef""; + Console.WriteLine(field); + } + static object Ptr_NonRef() => ""NonRef""; + static ref object Ptr_Ref() => ref field; + + static void Main() + { + M(&Ptr_NonRef); + M(&Ptr_Ref); + } +} +", expectedOutput: @" +NonRef +Ref"); + + verifier.VerifyIL("C.Main", @" +{ + // Code size 23 (0x17) + .maxstack 1 + IL_0000: ldftn ""object C.Ptr_NonRef()"" + IL_0006: call ""void C.M(delegate*)"" + IL_000b: ldftn ""ref object C.Ptr_Ref()"" + IL_0011: call ""void C.M(delegate*)"" + IL_0016: ret +} +"); + } + + [Fact] + public void Overloading_InvalidReturnRefness() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class C +{ + static void M1(delegate* ptr) => throw null; + static void M1(delegate* ptr) => throw null; + + static void M2(C[]> c) => throw null; + static void M2(C[]> c) => throw null; +} +"); + + comp.VerifyDiagnostics( + // (5,17): error CS0111: Type 'C' already defines a member called 'M1' with the same parameter types + // static void M1(delegate* ptr) => throw null; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "C").WithLocation(5, 17), + // (8,17): error CS0111: Type 'C' already defines a member called 'M2' with the same parameter types + // static void M2(C[]> c) => throw null; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "C").WithLocation(8, 17) + ); + } + + [Fact] + public void Overloading_ReturnNoBetterFunction() + { + var comp = CreateCompilationWithFunctionPointers(@" +interface I1 {} +interface I2 {} +unsafe class C : I1, I2 +{ + static void M1(delegate* ptr) => throw null; + static void M1(delegate* ptr) => throw null; + + static void M2(delegate* ptr) + { + M1(ptr); + } +} +"); + + comp.VerifyDiagnostics( + // (11,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.M1(delegate*)' and 'C.M1(delegate*)' + // M1(ptr); + Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("C.M1(delegate*)", "C.M1(delegate*)").WithLocation(11, 9) + ); + } + + [Fact] + public void Overloading_ParameterTypes() + { + var verifier = CompileAndVerifyFunctionPointers(@" +using System; +unsafe class C +{ + static void M(delegate* ptr) => ptr(""Object""); + static void M(delegate* ptr) => ptr(""String""); + + static void Main() + { + delegate* ptr1 = &Console.WriteLine; + delegate* ptr2 = &Console.WriteLine; + M(ptr1); + M(ptr2); + } +} +", expectedOutput: @" +Object +String"); + + verifier.VerifyIL("C.Main", expectedIL: @" +{ + // Code size 25 (0x19) + .maxstack 2 + .locals init (delegate* V_0) //ptr2 + IL_0000: ldftn ""void System.Console.WriteLine(object)"" + IL_0006: ldftn ""void System.Console.WriteLine(string)"" + IL_000c: stloc.0 + IL_000d: call ""void C.M(delegate*)"" + IL_0012: ldloc.0 + IL_0013: call ""void C.M(delegate*)"" + IL_0018: ret +} +"); + } + + [Fact] + public void Overloading_ValidParameterRefness() + { + var verifier = CompileAndVerifyFunctionPointers(@" +using System; +unsafe class C +{ + static object field = ""R""; + static void M(delegate* ptr) + { + Console.Write(field); + ref var local = ref field; + ptr(ref local); + Console.WriteLine(field); + } + static void M(delegate* ptr) => ptr(""NonRef""); + + static void Ptr(ref object param) => param = ""ef""; + + static void Main() + { + M(&Console.WriteLine); + M(&Ptr); + } +} +", expectedOutput: @" +NonRef +Ref"); + + verifier.VerifyIL("C.Main", expectedIL: @" +{ + // Code size 23 (0x17) + .maxstack 1 + IL_0000: ldftn ""void System.Console.WriteLine(object)"" + IL_0006: call ""void C.M(delegate*)"" + IL_000b: ldftn ""void C.Ptr(ref object)"" + IL_0011: call ""void C.M(delegate*)"" + IL_0016: ret +} +"); + } + + [Theory] + [InlineData("ref", "out")] + [InlineData("ref", "in")] + [InlineData("out", "in")] + public void Overloading_InvalidParameterRefness(string refKind1, string refKind2) + { + var comp = CreateCompilationWithFunctionPointers($@" +unsafe class C +{{ + static void M1(delegate*<{refKind1} object, void> ptr) => throw null; + static void M1(delegate*<{refKind2} object, void> ptr) => throw null; + + static void M2(C[]> c) => throw null; + static void M2(C[]> c) => throw null; +}} +"); + + comp.VerifyDiagnostics( + // (5,17): error CS0111: Type 'C' already defines a member called 'M1' with the same parameter types + // static void M1(delegate*<{refKind2} object, void> ptr) => throw null; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "C").WithLocation(5, 17), + // (8,17): error CS0111: Type 'C' already defines a member called 'M2' with the same parameter types + // static void M2(C[]> c) => throw null; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "C").WithLocation(8, 17) + ); + } + + [Fact] + public void Overloading_ParameterTypesNoBetterFunctionMember() + { + var comp = CreateCompilationWithFunctionPointers(@" +interface I1 {} +interface I2 {} +unsafe class C : I1, I2 +{ + static void M1(delegate*, void> ptr) => throw null; + static void M1(delegate*, void> ptr) => throw null; + + static void M2(delegate*, void> ptr) + { + M1(ptr); + } +} +"); + + comp.VerifyDiagnostics( + // (11,9): error CS0121: The call is ambiguous between the following methods or properties: 'C.M1(delegate*, void>)' and 'C.M1(delegate*, void>)' + // M1(ptr); + Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("C.M1(delegate*, void>)", "C.M1(delegate*, void>)").WithLocation(11, 9) + ); + } + + [Fact] + public void Override_CallingConventionMustMatch() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class Base +{ + protected virtual void M1(delegate* ptr) {} + protected virtual delegate* M2() => throw null; +} +unsafe class Derived : Base +{ + protected override void M1(delegate* cdecl ptr) {} + protected override delegate* cdecl M2() => throw null; +}"); + + comp.VerifyDiagnostics( + // (9,29): error CS0115: 'Derived.M1(delegate*)': no suitable method found to override + // protected override void M1(delegate* cdecl ptr) {} + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments("Derived.M1(delegate*)").WithLocation(9, 29), + // (10,46): error CS0508: 'Derived.M2()': return type must be 'delegate*' to match overridden member 'Base.M2()' + // protected override delegate* cdecl M2() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", "delegate*").WithLocation(10, 46) + ); + } + + [Theory] + [InlineData(" ", "ref ")] + [InlineData(" ", "out ")] + [InlineData(" ", "in ")] + [InlineData("ref ", "")] + [InlineData("ref ", "out ")] + [InlineData("ref ", "in ")] + [InlineData("out ", " ")] + [InlineData("out ", "ref ")] + [InlineData("out ", "in ")] + [InlineData("in ", " ")] + [InlineData("in ", "ref ")] + [InlineData("in ", "out ")] + public void Override_RefnessMustMatch_Parameters(string refKind1, string refKind2) + { + var comp = CreateCompilationWithFunctionPointers(@$" +unsafe class Base +{{ + protected virtual void M1(delegate*<{refKind1}string, void> ptr) {{}} + protected virtual delegate*<{refKind1}string, void> M2() => throw null; +}} +unsafe class Derived : Base +{{ + protected override void M1(delegate*<{refKind2}string, void> ptr) {{}} + protected override delegate*<{refKind2}string, void> M2() => throw null; +}}"); + + comp.VerifyDiagnostics( + // (9,29): error CS0115: 'Derived.M1(delegate*<{refKind2} string, void>)': no suitable method found to override + // protected override void M1(delegate*<{refKind2} string, void> ptr) {} + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments($"Derived.M1(delegate*<{refKindString(refKind2)}string, void>)").WithLocation(9, 29), + // (10,49): error CS0508: 'Derived.M2()': return type must be 'delegate*<{refKind1} string, void>' to match overridden member 'Base.M2()' + // protected override delegate*<{refKind2} string, void> M2() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", $"delegate*<{refKindString(refKind1)}string, void>").WithLocation(10, 48 + refKind2.Length) + ); + + static string refKindString(string orig) => orig == " " ? "" : orig; + } + + [Theory] + [InlineData(" ", "ref ")] + [InlineData(" ", "ref readonly ")] + [InlineData("ref ", " ")] + [InlineData("ref ", "ref readonly ")] + [InlineData("ref readonly ", " ")] + [InlineData("ref readonly ", "ref ")] + public void Override_RefnessMustMatch_Returns(string refKind1, string refKind2) + { + var comp = CreateCompilationWithFunctionPointers(@$" +unsafe class Base +{{ + protected virtual void M1(delegate*<{refKind1}string> ptr) {{}} + protected virtual delegate*<{refKind1}string> M2() => throw null; +}} +unsafe class Derived : Base +{{ + protected override void M1(delegate*<{refKind2}string> ptr) {{}} + protected override delegate*<{refKind2}string> M2() => throw null; +}}"); + + comp.VerifyDiagnostics( + // (9,29): error CS0115: 'Derived.M1(delegate*<{refKind2} string>)': no suitable method found to override + // protected override void M1(delegate*<{refKind2} string> ptr) {} + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments($"Derived.M1(delegate*)").WithLocation(9, 29), + // (10,49): error CS0508: 'Derived.M2()': return type must be 'delegate*<{refKind1} string>' to match overridden member 'Base.M2()' + // protected override delegate*<{refKind2} string> M2() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", $"delegate*").WithLocation(10, 42 + refKind2.Length) + ); + } + + [Fact] + public void Override_ParameterTypesMustMatch() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class Base +{ + protected virtual void M1(delegate* ptr) {{}} + protected virtual delegate* M2() => throw null; +} +unsafe class Derived : Base +{ + protected override void M1(delegate* ptr) {{}} + protected override delegate* M2() => throw null; +}"); + + comp.VerifyDiagnostics( + // (9,29): error CS0115: 'Derived.M1(delegate*)': no suitable method found to override + // protected override void M1(delegate* ptr) {} + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments("Derived.M1(delegate*)").WithLocation(9, 29), + // (10,48): error CS0508: 'Derived.M2()': return type must be 'delegate*' to match overridden member 'Base.M2()' + // protected override delegate* M2() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", "delegate*").WithLocation(10, 48) + ); + } + + [Fact] + public void Override_ReturnTypesMustMatch() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class Base +{ + protected virtual void M1(delegate* ptr) {{}} + protected virtual delegate* M2() => throw null; +} +unsafe class Derived : Base +{ + protected override void M1(delegate* ptr) {{}} + protected override delegate* M2() => throw null; +}"); + + comp.VerifyDiagnostics( + // (9,29): error CS0115: 'Derived.M1(delegate*)': no suitable method found to override + // protected override void M1(delegate* ptr) {} + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments("Derived.M1(delegate*)").WithLocation(9, 29), + // (10,42): error CS0508: 'Derived.M2()': return type must be 'delegate*' to match overridden member 'Base.M2()' + // protected override delegate* M2() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", "delegate*").WithLocation(10, 42) + ); + } + + [Theory] + [InlineData("nint", "System.IntPtr")] + [InlineData("dynamic", "object")] + public void Override_AllowedTypeDifferences(string type1, string type2) + { + var comp = CreateCompilationWithFunctionPointers(@$" +unsafe class Base +{{ + protected virtual void M1(delegate*<{type1}> ptr) {{}} + protected virtual delegate*<{type1}> M2() => throw null; + protected virtual void M3(delegate*<{type1}, void> ptr) {{}} + protected virtual delegate*<{type1}, void> M4() => throw null; + protected virtual void M5(delegate*<{type2}> ptr) {{}} + protected virtual delegate*<{type2}> M6() => throw null; + protected virtual void M7(delegate*<{type2}, void> ptr) {{}} + protected virtual delegate*<{type2}, void> M8() => throw null; +}} +unsafe class Derived : Base +{{ + protected override void M1(delegate*<{type2}> ptr) {{}} + protected override delegate*<{type2}> M2() => throw null; + protected override void M3(delegate*<{type2}, void> ptr) {{}} + protected override delegate*<{type2}, void> M4() => throw null; + protected override void M5(delegate*<{type1}> ptr) {{}} + protected override delegate*<{type1}> M6() => throw null; + protected override void M7(delegate*<{type1}, void> ptr) {{}} + protected override delegate*<{type1}, void> M8() => throw null; +}}"); + + comp.VerifyDiagnostics( + ); + } + + [Fact] + public void Override_TupleNameChanges() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class Base +{ + protected virtual void M1(delegate*<(int, string)> ptr) {{}} + protected virtual delegate*<(int, string)> M2() => throw null; + protected virtual void M3(delegate*<(int, string), void> ptr) {{}} + protected virtual delegate*<(int, string), void> M4() => throw null; + protected virtual void M5(delegate*<(int i, string s)> ptr) {{}} + protected virtual delegate*<(int i, string s)> M6() => throw null; + protected virtual void M7(delegate*<(int i, string s), void> ptr) {{}} + protected virtual delegate*<(int i, string s), void> M8() => throw null; +} +unsafe class Derived : Base +{ + protected override void M1(delegate*<(int i, string s)> ptr) {{}} + protected override delegate*<(int i, string s)> M2() => throw null; + protected override void M3(delegate*<(int i, string s), void> ptr) {{}} + protected override delegate*<(int i, string s), void> M4() => throw null; + protected override void M5(delegate*<(int, string)> ptr) {{}} + protected override delegate*<(int, string)> M6() => throw null; + protected override void M7(delegate*<(int, string), void> ptr) {{}} + protected override delegate*<(int, string), void> M8() => throw null; +}"); + + comp.VerifyDiagnostics( + // (15,29): error CS8139: 'Derived.M1(delegate*<(int i, string s)>)': cannot change tuple element names when overriding inherited member 'Base.M1(delegate*<(int, string)>)' + // protected override void M1(delegate*<(int i, string s)> ptr) {} + Diagnostic(ErrorCode.ERR_CantChangeTupleNamesOnOverride, "M1").WithArguments("Derived.M1(delegate*<(int i, string s)>)", "Base.M1(delegate*<(int, string)>)").WithLocation(15, 29), + // (16,53): error CS8139: 'Derived.M2()': cannot change tuple element names when overriding inherited member 'Base.M2()' + // protected override delegate*<(int i, string s)> M2() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeTupleNamesOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()").WithLocation(16, 53), + // (17,29): error CS8139: 'Derived.M3(delegate*<(int i, string s), void>)': cannot change tuple element names when overriding inherited member 'Base.M3(delegate*<(int, string), void>)' + // protected override void M3(delegate*<(int i, string s), void> ptr) {} + Diagnostic(ErrorCode.ERR_CantChangeTupleNamesOnOverride, "M3").WithArguments("Derived.M3(delegate*<(int i, string s), void>)", "Base.M3(delegate*<(int, string), void>)").WithLocation(17, 29), + // (18,59): error CS8139: 'Derived.M4()': cannot change tuple element names when overriding inherited member 'Base.M4()' + // protected override delegate*<(int i, string s), void> M4() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeTupleNamesOnOverride, "M4").WithArguments("Derived.M4()", "Base.M4()").WithLocation(18, 59) + ); + } + + [Fact] + public void Override_NullabilityChanges_NoRefs() + { + var comp = CreateCompilationWithFunctionPointers(@" +#nullable enable +unsafe class Base +{ + protected virtual void M1(delegate* ptr) {{}} + protected virtual delegate* M2() => throw null!; + protected virtual void M3(delegate* ptr) {{}} + protected virtual delegate* M4() => throw null!; + protected virtual void M5(delegate* ptr) {{}} + protected virtual delegate* M6() => throw null!; + protected virtual void M7(delegate* ptr) {{}} + protected virtual delegate* M8() => throw null!; +} +unsafe class Derived : Base +{ + protected override void M1(delegate* ptr) {{}} + protected override delegate* M2() => throw null!; + protected override void M3(delegate* ptr) {{}} + protected override delegate* M4() => throw null!; + protected override void M5(delegate* ptr) {{}} + protected override delegate* M6() => throw null!; + protected override void M7(delegate* ptr) {{}} + protected override delegate* M8() => throw null!; +}"); + + comp.VerifyDiagnostics( + // (16,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M1(delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 29), + // (19,48): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override delegate* M4() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M4").WithLocation(19, 48), + // (21,43): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override delegate* M6() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 43), + // (22,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M7(delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29) + ); + } + + [Fact] + public void Override_NullabilityChanges_RefsInParameterReturnTypes() + { + var comp = CreateCompilationWithFunctionPointers(@" +#nullable enable +unsafe class Base +{ + protected virtual void M1(delegate* ptr) {{}} + protected virtual delegate* M2() => throw null!; + protected virtual void M3(delegate* ptr) {{}} + protected virtual delegate* M4() => throw null!; + protected virtual void M5(delegate* ptr) {{}} + protected virtual delegate* M6() => throw null!; + protected virtual void M7(delegate* ptr) {{}} + protected virtual delegate* M8() => throw null!; +} +unsafe class Derived : Base +{ + protected override void M1(delegate* ptr) {{}} + protected override delegate* M2() => throw null!; + protected override void M3(delegate* ptr) {{}} + protected override delegate* M4() => throw null!; + protected override void M5(delegate* ptr) {{}} + protected override delegate* M6() => throw null!; + protected override void M7(delegate* ptr) {{}} + protected override delegate* M8() => throw null!; +}"); + + comp.VerifyDiagnostics( + // (16,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M1(delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 29), + // (17,46): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override delegate* M2() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M2").WithLocation(17, 46), + // (18,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M3(delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M3").WithArguments("ptr").WithLocation(18, 29), + // (19,52): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override delegate* M4() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M4").WithLocation(19, 52), + // (20,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M5(delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("ptr").WithLocation(20, 29), + // (21,47): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override delegate* M6() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 47), + // (22,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M7(delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29), + // (23,53): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override delegate* M8() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M8").WithLocation(23, 53) + ); + } + + [Fact] + public void Override_NullabilityChanges_PointerByRef() + { + var comp = CreateCompilationWithFunctionPointers(@" +#nullable enable +unsafe class Base +{ + protected virtual void M1(ref delegate* ptr) {{}} + protected virtual ref delegate* M2() => throw null!; + protected virtual void M3(ref delegate* ptr) {{}} + protected virtual ref delegate* M4() => throw null!; + protected virtual void M5(ref delegate* ptr) {{}} + protected virtual ref delegate* M6() => throw null!; + protected virtual void M7(ref delegate* ptr) {{}} + protected virtual ref delegate* M8() => throw null!; +} +unsafe class Derived : Base +{ + protected override void M1(ref delegate* ptr) {{}} + protected override ref delegate* M2() => throw null!; + protected override void M3(ref delegate* ptr) {{}} + protected override ref delegate* M4() => throw null!; + protected override void M5(ref delegate* ptr) {{}} + protected override ref delegate* M6() => throw null!; + protected override void M7(ref delegate* ptr) {{}} + protected override ref delegate* M8() => throw null!; +}"); + + comp.VerifyDiagnostics( + // (16,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M1(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 29), + // (17,46): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override ref delegate* M2() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M2").WithLocation(17, 46), + // (18,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M3(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M3").WithArguments("ptr").WithLocation(18, 29), + // (19,52): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override ref delegate* M4() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M4").WithLocation(19, 52), + // (20,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M5(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("ptr").WithLocation(20, 29), + // (21,47): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override ref delegate* M6() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 47), + // (22,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // protected override void M7(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29), + // (23,53): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // protected override ref delegate* M8() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M8").WithLocation(23, 53) + ); + } + + [Fact] + public void Override_SingleDimensionArraySizesInMetadata() + { + + var il = @" +.class public auto ansi abstract beforefieldinit Base + extends [mscorlib]System.Object +{ + // Methods + .method public hidebysig newslot abstract virtual + void M1 (method void *(int32[0...]) param) cil managed + { + } + + .method public hidebysig newslot abstract virtual + method void *(int32[0...]) M2 () cil managed + { + } + + .method public hidebysig newslot abstract virtual + void M3 (method int32[0...] *() param) cil managed + { + } + + .method public hidebysig newslot abstract virtual + method int32[0...] *() M4 () cil managed + { + } + + .method family hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } +}"; + + var source = @" +unsafe class Derived : Base +{ + public override void M1(delegate* param) => throw null; + public override delegate* M2() => throw null; + public override void M3(delegate* param) => throw null; + public override delegate* M4() => throw null; +}"; + + var comp = CreateCompilationWithFunctionPointersAndIl(source, il); + comp.VerifyDiagnostics( + // (2,14): error CS0534: 'Derived' does not implement inherited abstract member 'Base.M1(delegate*)' + // unsafe class Derived : Base + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "Derived").WithArguments("Derived", "Base.M1(delegate*)").WithLocation(2, 14), + // (2,14): error CS0534: 'Derived' does not implement inherited abstract member 'Base.M3(delegate*)' + // unsafe class Derived : Base + Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "Derived").WithArguments("Derived", "Base.M3(delegate*)").WithLocation(2, 14), + // (4,26): error CS0115: 'Derived.M1(delegate*)': no suitable method found to override + // public override void M1(delegate* param) => throw null; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments("Derived.M1(delegate*)").WithLocation(4, 26), + // (5,44): error CS0508: 'Derived.M2()': return type must be 'delegate*' to match overridden member 'Base.M2()' + // public override delegate* M2() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", "delegate*").WithLocation(5, 44), + // (6,26): error CS0115: 'Derived.M3(delegate*)': no suitable method found to override + // public override void M3(delegate* param) => throw null; + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M3").WithArguments("Derived.M3(delegate*)").WithLocation(6, 26), + // (7,38): error CS0508: 'Derived.M4()': return type must be 'delegate*' to match overridden member 'Base.M4()' + // public override delegate* M4() => throw null; + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M4").WithArguments("Derived.M4()", "Base.M4()", "delegate*").WithLocation(7, 38) + ); + } + + [Fact] + public void Override_ArraySizesInMetadata() + { + var il = @" +.class public auto ansi abstract beforefieldinit Base + extends [mscorlib]System.Object +{ + // Methods + .method public hidebysig newslot abstract virtual + void M1 (method void *(int32[5...5,2...4]) param) cil managed + { + } + + .method public hidebysig newslot abstract virtual + method void *(int32[5...5,2...4]) M2 () cil managed + { + } + + .method public hidebysig newslot abstract virtual + void M3 (method int32[5...5,2...4] *() param) cil managed + { + } + + .method public hidebysig newslot abstract virtual + method int32[5...5,2...4] *() M4 () cil managed + { + } + + .method family hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + } +}"; + + var source = @" +using System; +unsafe class Derived : Base +{ + private static void MultiDimensionParamFunc(int[,] param) { } + private static int[,] MultiDimensionReturnFunc() => null; + + public override void M1(delegate* param) + { + Console.WriteLine(""Multi-dimension array param as param""); + param(null); + } + + public override delegate* M2() + { + Console.WriteLine(""Multi-dimension array param as return""); + return &MultiDimensionParamFunc; + } + + public override void M3(delegate* param) + { + Console.WriteLine(""Multi-dimension array return as param""); + _ = param(); + } + + public override delegate* M4() + { + Console.WriteLine(""Multi-dimension array return as return""); + return &MultiDimensionReturnFunc; + } + + public static void Main() + { + var d = new Derived(); + d.M1(&MultiDimensionParamFunc); + var ptr1 = d.M2(); + ptr1(null); + d.M3(&MultiDimensionReturnFunc); + var ptr2 = d.M4(); + _ = ptr2(); + } +}"; + + var verifier = CompileAndVerifyFunctionPointersWithIl(source, il, expectedOutput: @" +Multi-dimension array param as param +Multi-dimension array param as return +Multi-dimension array return as param +Multi-dimension array return as return +"); + + verifier.VerifyIL("Derived.M1", expectedIL: @" +{ + // Code size 20 (0x14) + .maxstack 2 + .locals init (delegate* V_0) + IL_0000: ldstr ""Multi-dimension array param as param"" + IL_0005: call ""void System.Console.WriteLine(string)"" + IL_000a: ldarg.1 + IL_000b: stloc.0 + IL_000c: ldnull + IL_000d: ldloc.0 + IL_000e: calli ""delegate*"" + IL_0013: ret +} +"); + + verifier.VerifyIL("Derived.M2", expectedIL: @" +{ + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldstr ""Multi-dimension array param as return"" + IL_0005: call ""void System.Console.WriteLine(string)"" + IL_000a: ldftn ""void Derived.MultiDimensionParamFunc(int[,])"" + IL_0010: ret +} +"); + + verifier.VerifyIL("Derived.M3", expectedIL: @" +{ + // Code size 18 (0x12) + .maxstack 1 + IL_0000: ldstr ""Multi-dimension array return as param"" + IL_0005: call ""void System.Console.WriteLine(string)"" + IL_000a: ldarg.1 + IL_000b: calli ""delegate*"" + IL_0010: pop + IL_0011: ret +} +"); + + verifier.VerifyIL("Derived.M4", expectedIL: @" +{ + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldstr ""Multi-dimension array return as return"" + IL_0005: call ""void System.Console.WriteLine(string)"" + IL_000a: ldftn ""int[,] Derived.MultiDimensionReturnFunc()"" + IL_0010: ret +} +"); + + verifier.VerifyIL("Derived.Main", expectedIL: @" +{ + // Code size 55 (0x37) + .maxstack 3 + .locals init (delegate* V_0) + IL_0000: newobj ""Derived..ctor()"" + IL_0005: dup + IL_0006: ldftn ""void Derived.MultiDimensionParamFunc(int[,])"" + IL_000c: callvirt ""void Base.M1(delegate*)"" + IL_0011: dup + IL_0012: callvirt ""delegate* Base.M2()"" + IL_0017: stloc.0 + IL_0018: ldnull + IL_0019: ldloc.0 + IL_001a: calli ""delegate*"" + IL_001f: dup + IL_0020: ldftn ""int[,] Derived.MultiDimensionReturnFunc()"" + IL_0026: callvirt ""void Base.M3(delegate*)"" + IL_002b: callvirt ""delegate* Base.M4()"" + IL_0030: calli ""delegate*"" + IL_0035: pop + IL_0036: ret +} +"); + + var comp = (CSharpCompilation)verifier.Compilation; + + var m1 = comp.GetMember("Derived.M1"); + var m2 = comp.GetMember("Derived.M2"); + var m3 = comp.GetMember("Derived.M3"); + var m4 = comp.GetMember("Derived.M4"); + + var funcPtr = (FunctionPointerTypeSymbol)m1.Parameters.Single().Type; + CommonVerifyFunctionPointer(funcPtr); + verifyArray(funcPtr.Signature.Parameters.Single().Type); + + funcPtr = (FunctionPointerTypeSymbol)m2.ReturnType; + CommonVerifyFunctionPointer(funcPtr); + verifyArray(funcPtr.Signature.Parameters.Single().Type); + + funcPtr = (FunctionPointerTypeSymbol)m3.Parameters.Single().Type; + CommonVerifyFunctionPointer(funcPtr); + verifyArray(funcPtr.Signature.ReturnType); + + funcPtr = (FunctionPointerTypeSymbol)m4.ReturnType; + CommonVerifyFunctionPointer(funcPtr); + verifyArray(funcPtr.Signature.ReturnType); + + static void verifyArray(TypeSymbol type) + { + var array = (ArrayTypeSymbol)type; + Assert.False(array.IsSZArray); + Assert.Equal(2, array.Rank); + Assert.Equal(5, array.LowerBounds[0]); + Assert.Equal(1, array.Sizes[0]); + Assert.Equal(2, array.LowerBounds[1]); + Assert.Equal(3, array.Sizes[1]); + } + } + private static readonly Guid s_guid = new Guid("97F4DBD4-F6D1-4FAD-91B3-1001F92068E5"); private static readonly BlobContentId s_contentId = new BlobContentId(s_guid, 0x04030201); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index e48bab5e6e89d..56b853630c4b3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -1381,7 +1381,6 @@ void M(bool b) } }"); - // Implicit pointer conversions are not currently handled, ptr3/4 and ptr7/8 should warn: https://github.com/dotnet/roslyn/issues/39865 comp.VerifyDiagnostics( // (12,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' // _ = b ? ptr1 : ptr3; @@ -1389,12 +1388,18 @@ void M(bool b) // (13,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' // _ = b ? ptr1 : ptr4; Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr1 : ptr4").WithArguments("delegate*", "delegate*").WithLocation(13, 13), + // (14,24): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // _ = b ? ptr3 : ptr4; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr4").WithArguments("delegate*", "delegate*").WithLocation(14, 24), // (21,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' // _ = b ? ptr5 : ptr7; Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr5 : ptr7").WithArguments("delegate*", "delegate*").WithLocation(21, 13), // (22,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' // _ = b ? ptr5 : ptr8; - Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr5 : ptr8").WithArguments("delegate*", "delegate*").WithLocation(22, 13) + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr5 : ptr8").WithArguments("delegate*", "delegate*").WithLocation(22, 13), + // (23,24): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // _ = b ? ptr7 : ptr8; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr8").WithArguments("delegate*", "delegate*").WithLocation(23, 24) ); var tree = comp.SyntaxTrees[0]; diff --git a/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs b/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs index d78afe56e28d9..891cf5cec2bd3 100644 --- a/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs +++ b/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs @@ -25,6 +25,7 @@ internal enum TypeCompareKind IgnoreNullableModifiersForReferenceTypes = 8, ObliviousNullableModifierMatchesAny = 16, IgnoreNativeIntegers = 32, + FunctionPointerRefMatchesOutInRefReadonly = 64, AllNullableIgnoreOptions = IgnoreNullableModifiersForReferenceTypes | ObliviousNullableModifierMatchesAny, AllIgnoreOptions = IgnoreCustomModifiersAndArraySizesAndLowerBounds | IgnoreDynamic | IgnoreTupleNames | AllNullableIgnoreOptions | IgnoreNativeIntegers, From 3d33fe415f68352bbbdc9bee32e83625546d67ab Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 14 May 2020 10:00:28 -0700 Subject: [PATCH 07/11] Add nullable tests --- .../Portable/Binder/Binder_Invocation.cs | 1 - .../Portable/FlowAnalysis/NullableWalker.cs | 1 - .../Symbols/Metadata/PE/TupleTypeDecoder.cs | 1 - .../CodeGen/CodeGenFunctionPointersTests.cs | 57 ++++++++++++++++++- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 690535a6b2c69..3b02f23881cbe 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -270,7 +270,6 @@ private BoundExpression BindInvocationExpression( } else if (boundExpression.Type?.Kind == SymbolKind.FunctionPointer) { - // PROTOTYPE(func-ptr): Test nullable related features ReportSuppressionIfNeeded(boundExpression, diagnostics); result = BindFunctionPointerInvocation(node, boundExpression, analyzedArguments, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 1f6b2a12709d3..8499da311c6db 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8888,7 +8888,6 @@ public override BoundNode VisitAwaitableInfo(BoundAwaitableInfo node) return null; } - // PROTOTYPE(func-ptr): Test nullable related features public override BoundNode VisitFunctionPointerInvocation(BoundFunctionPointerInvocation node) { _ = Visit(node.InvokedExpression); diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs index a97f566144d99..49516fdf6bcdd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/TupleTypeDecoder.cs @@ -62,7 +62,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE /// /// /// - // PROTOTYPE(func-ptr): Implement and test in function pointers. Also dynamic and nullable internal struct TupleTypeDecoder { private readonly ImmutableArray _elementNames; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index 5c46630c97c2d..0cfb7165f59dc 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -1232,7 +1232,7 @@ public unsafe static void Call(delegate* {convention} pt }}"; var verifier = CompileAndVerifyFunctionPointersWithIl(source, ilStub, expectedOutput: "Hello World"); - // PROTOTYPE(func-ptr): Add calling convention when the formatter supports it + // https://github.com/dotnet/roslyn/issues/39865: Add calling convention when the formatter supports it verifier.VerifyIL($"Caller.Call(delegate*)", @" { // Code size 24 (0x18) @@ -6480,6 +6480,61 @@ static void verifyArray(TypeSymbol type) } } + [Fact] + public void NullableUsageWarnings() + { + var comp = CreateCompilationWithFunctionPointers(@" +#nullable enable +unsafe public class C +{ + static void M1(delegate* ptr1) + { + _ = ptr1(null, null); + _ = ptr1("""", null).ToString(); + delegate* ptr2 = ptr1; + delegate* ptr3 = ptr1; + } + + static void M2(delegate* ptr1) + { + string? str1 = null; + ptr1(ref str1); + string str2 = """"; + ref string? str3 = ref ptr1(ref str2); + delegate* ptr2 = ptr1; + delegate* ptr3 = ptr1; + } +} +"); + + comp.VerifyDiagnostics( + // (7,18): warning CS8625: Cannot convert null literal to non-nullable reference type. + // _ = ptr1(null, null); + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 18), + // (8,13): warning CS8602: Dereference of a possibly null reference. + // _ = ptr1("", null).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, @"ptr1("""", null)").WithLocation(8, 13), + // (9,53): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // delegate* ptr2 = ptr1; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr1").WithArguments("delegate*", "delegate*").WithLocation(9, 53), + // (10,51): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // delegate* ptr3 = ptr1; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr1").WithArguments("delegate*", "delegate*").WithLocation(10, 51), + // (16,18): warning CS8601: Possible null reference assignment. + // ptr1(ref str1); + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "str1").WithLocation(16, 18), + // (18,32): warning CS8619: Nullability of reference types in value of type 'string' doesn't match target type 'string?'. + // ref string? str3 = ref ptr1(ref str2); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr1(ref str2)").WithArguments("string", "string?").WithLocation(18, 32), + // (19,51): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // delegate* ptr2 = ptr1; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr1").WithArguments("delegate*", "delegate*").WithLocation(19, 51), + // (20,51): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // delegate* ptr3 = ptr1; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr1").WithArguments("delegate*", "delegate*").WithLocation(20, 51) + ); + } + private static readonly Guid s_guid = new Guid("97F4DBD4-F6D1-4FAD-91B3-1001F92068E5"); private static readonly BlobContentId s_contentId = new BlobContentId(s_guid, 0x04030201); From 3f827dd6ab293da813231d9cb10e7f87b3934968 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 18 May 2020 16:14:25 -0700 Subject: [PATCH 08/11] Respond to PR feedback. Added additional documentation for the TypeCompareKind flag, and targetted tests. Added more testing of other scenarios. --- .../FunctionPointerMethodSymbol.cs | 2 +- .../FunctionPointerParameterSymbol.cs | 2 +- .../FunctionPointerTypeSymbol.cs | 37 ++- .../Symbols/MemberSignatureComparer.cs | 7 +- .../Symbols/Source/CustomModifierUtils.cs | 2 +- .../CodeGen/CodeGenFunctionPointersTests.cs | 293 +++++++++++++----- .../Semantic/Semantics/AccessCheckTests.cs | 21 ++ .../Symbols/FunctionPointerTypeSymbolTests.cs | 31 ++ .../Core/Portable/Symbols/TypeCompareKind.cs | 8 + 9 files changed, 311 insertions(+), 92 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs index 3b15f07b4ebb8..b29b4b47b9e9d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs @@ -397,7 +397,7 @@ public override int GetHashCode() } internal int GetHashCodeNoParameters() - => Hash.Combine(ReturnType, Hash.Combine(CallingConvention.GetHashCode(), RefKind.GetHashCode())); + => Hash.Combine(ReturnType, Hash.Combine(CallingConvention.GetHashCode(), FunctionPointerTypeSymbol.GetRefKindForHashCode(RefKind).GetHashCode())); internal override CallingConvention CallingConvention { get; } public override bool ReturnsVoid => ReturnTypeWithAnnotations.IsVoidType(); diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs index 98cf57e20a655..c2aebe52064fa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs @@ -60,7 +60,7 @@ public override int GetHashCode() } internal int MethodHashCode() - => Hash.Combine(TypeWithAnnotations.GetHashCode(), RefKind.GetHashCode()); + => Hash.Combine(TypeWithAnnotations.GetHashCode(), FunctionPointerTypeSymbol.GetRefKindForHashCode(RefKind).GetHashCode()); public override ImmutableArray Locations => ImmutableArray.Empty; public override ImmutableArray DeclaringSyntaxReferences => ImmutableArray.Empty; diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs index 83c1d49984bbb..0b24fdc4e6751 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs @@ -157,22 +157,29 @@ internal override TypeSymbol SetNullabilityForReferenceTypes(Func + /// For scenarios such as overriding with differing ref kinds (such as out vs in or ref) + /// we need to compare function pointer parameters assuming that Ref matches RefReadonly/In + /// and Out. This is done because you cannot overload on ref vs out vs in in regular method + /// signatures, and we are disallowing similar overloads in source with function pointers. + /// internal static bool RefKindEquals(TypeCompareKind compareKind, RefKind refKind1, RefKind refKind2) - { - if ((compareKind & TypeCompareKind.FunctionPointerRefMatchesOutInRefReadonly) != 0) - { - return (refKind1, refKind2) switch - { - (RefKind.None, RefKind.None) => true, - (RefKind.None, _) => false, - (_, RefKind.None) => false, - _ => true - }; - } - else + => (compareKind & TypeCompareKind.FunctionPointerRefMatchesOutInRefReadonly) != 0 + ? (refKind1 == RefKind.None) == (refKind2 == RefKind.None) + : refKind1 == refKind2; + + /// + /// For scenarios such as overriding with differing ref kinds (such as out vs in or ref) + /// we need to compare function pointer parameters assuming that Ref matches RefReadonly/In + /// and Out. For that reason, we must also ensure that GetHashCode returns equal hashcodes + /// for types that only differ by the type of ref they have. + /// + internal static RefKind GetRefKindForHashCode(RefKind refKind) + => refKind switch { - return refKind1 == refKind2; - } - } + RefKind.Out => RefKind.Ref, + RefKind.In => RefKind.Ref, + _ => refKind + }; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index 21af5570fe7a2..18e2fd35c42b4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -313,6 +313,8 @@ private MemberSignatureComparer( _considerCallingConvention = considerCallingConvention; _considerRefKindDifferences = considerRefKindDifferences; _typeComparison = typeComparison; + Debug.Assert((_typeComparison & TypeCompareKind.FunctionPointerRefMatchesOutInRefReadonly) == 0, + $"Rely on the {nameof(considerRefKindDifferences)} flag to set this to ensure all cases are handled."); if (!considerRefKindDifferences) { _typeComparison |= TypeCompareKind.FunctionPointerRefMatchesOutInRefReadonly; @@ -460,11 +462,6 @@ public int GetHashCode(Symbol member) #endregion - public static bool HaveSameReturnTypes(MethodSymbol member1, MethodSymbol member2, TypeCompareKind typeComparison) - { - return HaveSameReturnTypes(member1, GetTypeMap(member1), member2, GetTypeMap(member2), typeComparison); - } - private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol member2, TypeMap typeMap2, TypeCompareKind typeComparison) { RefKind refKind1; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs index 424420c67847e..01b00b5b5e98a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs @@ -103,7 +103,7 @@ internal static TypeSymbol CopyTypeCustomModifiers(TypeSymbol sourceType, TypeSy Debug.Assert(resultType.Equals(sourceType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); // Same custom modifiers as source type. // Same object/dynamic, nullability and tuple names as destination type. - Debug.Assert(resultType.Equals(destinationType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNativeIntegers)); + Debug.Assert(resultType.Equals(destinationType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)); return resultType; } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index 0cfb7165f59dc..6d7658a40edb7 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -5980,37 +5980,110 @@ protected override void M1(delegate* ptr) {{}} ); } - [Theory] - [InlineData("nint", "System.IntPtr")] - [InlineData("dynamic", "object")] - public void Override_AllowedTypeDifferences(string type1, string type2) + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/44358")] + public void Override_NintIntPtrDifferences() { - var comp = CreateCompilationWithFunctionPointers(@$" + var comp = CreateCompilationWithFunctionPointers(@" unsafe class Base -{{ - protected virtual void M1(delegate*<{type1}> ptr) {{}} - protected virtual delegate*<{type1}> M2() => throw null; - protected virtual void M3(delegate*<{type1}, void> ptr) {{}} - protected virtual delegate*<{type1}, void> M4() => throw null; - protected virtual void M5(delegate*<{type2}> ptr) {{}} - protected virtual delegate*<{type2}> M6() => throw null; - protected virtual void M7(delegate*<{type2}, void> ptr) {{}} - protected virtual delegate*<{type2}, void> M8() => throw null; -}} +{ + protected virtual void M1(delegate* ptr) {} + protected virtual delegate* M2() => throw null; + protected virtual void M3(delegate* ptr) {} + protected virtual delegate* M4() => throw null; + protected virtual void M5(delegate* ptr) {} + protected virtual delegate* M6() => throw null; + protected virtual void M7(delegate* ptr) {} + protected virtual delegate* M8() => throw null; +} unsafe class Derived : Base -{{ - protected override void M1(delegate*<{type2}> ptr) {{}} - protected override delegate*<{type2}> M2() => throw null; - protected override void M3(delegate*<{type2}, void> ptr) {{}} - protected override delegate*<{type2}, void> M4() => throw null; - protected override void M5(delegate*<{type1}> ptr) {{}} - protected override delegate*<{type1}> M6() => throw null; - protected override void M7(delegate*<{type1}, void> ptr) {{}} - protected override delegate*<{type1}, void> M8() => throw null; -}}"); +{ + protected override void M1(delegate* ptr) {} + protected override delegate* M2() => throw null; + protected override void M3(delegate* ptr) {} + protected override delegate* M4() => throw null; + protected override void M5(delegate* ptr) {} + protected override delegate* M6() => throw null; + protected override void M7(delegate* ptr) {} + protected override delegate* M8() => throw null; +}"); comp.VerifyDiagnostics( ); + + assertMethods(comp.SourceModule); + CompileAndVerify(comp, symbolValidator: assertMethods); + + static void assertMethods(ModuleSymbol module) + { + var derived = module.GlobalNamespace.GetMember("Derived"); + assertMethod(derived, "M1", "void Derived.M1(delegate* ptr)"); + assertMethod(derived, "M2", "delegate* Derived.M2()"); + assertMethod(derived, "M3", "void Derived.M3(delegate* ptr)"); + assertMethod(derived, "M4", "delegate* Derived.M4()"); + assertMethod(derived, "M5", "void Derived.M5(delegate* ptr)"); + assertMethod(derived, "M6", "delegate* Derived.M6()"); + assertMethod(derived, "M7", "void Derived.M7(delegate* ptr)"); + assertMethod(derived, "M8", "delegate* Derived.M8()"); + } + + static void assertMethod(NamedTypeSymbol derived, string methodName, string expectedSignature) + { + var m = derived.GetMember(methodName); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedSignature, m.ToTestDisplayString(includeNonNullable: true)); + } + } + + [Fact] + public void Override_ObjectDynamicDifferences() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class Base +{ + protected virtual void M1(delegate* ptr) {} + protected virtual delegate* M2() => throw null; + protected virtual void M3(delegate* ptr) {} + protected virtual delegate* M4() => throw null; + protected virtual void M5(delegate* ptr) {} + protected virtual delegate* M6() => throw null; + protected virtual void M7(delegate* ptr) {} + protected virtual delegate* M8() => throw null; +} +unsafe class Derived : Base +{ + protected override void M1(delegate* ptr) {} + protected override delegate* M2() => throw null; + protected override void M3(delegate* ptr) {} + protected override delegate* M4() => throw null; + protected override void M5(delegate* ptr) {} + protected override delegate* M6() => throw null; + protected override void M7(delegate* ptr) {} + protected override delegate* M8() => throw null; +}"); + + comp.VerifyDiagnostics( + ); + + assertMethods(comp.SourceModule); + CompileAndVerify(comp, symbolValidator: assertMethods); + + static void assertMethods(ModuleSymbol module) + { + var derived = module.GlobalNamespace.GetMember("Derived"); + assertMethod(derived, "M1", "void Derived.M1(delegate* ptr)"); + assertMethod(derived, "M2", "delegate* Derived.M2()"); + assertMethod(derived, "M3", "void Derived.M3(delegate* ptr)"); + assertMethod(derived, "M4", "delegate* Derived.M4()"); + assertMethod(derived, "M5", "void Derived.M5(delegate* ptr)"); + assertMethod(derived, "M6", "delegate* Derived.M6()"); + assertMethod(derived, "M7", "void Derived.M7(delegate* ptr)"); + assertMethod(derived, "M8", "delegate* Derived.M8()"); + } + + static void assertMethod(NamedTypeSymbol derived, string methodName, string expectedSignature) + { + var m = derived.GetMember(methodName); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedSignature, m.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -6054,6 +6127,21 @@ protected override void M7(delegate*<(int, string), void> ptr) {{}} // protected override delegate*<(int i, string s), void> M4() => throw null; Diagnostic(ErrorCode.ERR_CantChangeTupleNamesOnOverride, "M4").WithArguments("Derived.M4()", "Base.M4()").WithLocation(18, 59) ); + + assertMethod("M1", "void Derived.M1(delegate*<(System.Int32 i, System.String s)> ptr)"); + assertMethod("M2", "delegate*<(System.Int32 i, System.String s)> Derived.M2()"); + assertMethod("M3", "void Derived.M3(delegate*<(System.Int32 i, System.String s), System.Void> ptr)"); + assertMethod("M4", "delegate*<(System.Int32 i, System.String s), System.Void> Derived.M4()"); + assertMethod("M5", "void Derived.M5(delegate*<(System.Int32, System.String)> ptr)"); + assertMethod("M6", "delegate*<(System.Int32, System.String)> Derived.M6()"); + assertMethod("M7", "void Derived.M7(delegate*<(System.Int32, System.String), System.Void> ptr)"); + assertMethod("M8", "delegate*<(System.Int32, System.String), System.Void> Derived.M8()"); + + void assertMethod(string methodName, string expectedSignature) + { + var m = comp.GetMember($"Derived.{methodName}"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedSignature, m.ToTestDisplayString()); + } } [Fact] @@ -6098,6 +6186,28 @@ protected override void M7(delegate* ptr) {{}} // protected override void M7(delegate* ptr) {{}} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29) ); + + assertMethods(comp.SourceModule); + CompileAndVerify(comp, symbolValidator: assertMethods); + + static void assertMethods(ModuleSymbol module) + { + var derived = module.GlobalNamespace.GetMember("Derived"); + assertMethod(derived, "M1", "void Derived.M1(delegate* ptr)"); + assertMethod(derived, "M2", "delegate* Derived.M2()"); + assertMethod(derived, "M3", "void Derived.M3(delegate* ptr)"); + assertMethod(derived, "M4", "delegate* Derived.M4()"); + assertMethod(derived, "M5", "void Derived.M5(delegate* ptr)"); + assertMethod(derived, "M6", "delegate* Derived.M6()"); + assertMethod(derived, "M7", "void Derived.M7(delegate* ptr)"); + assertMethod(derived, "M8", "delegate* Derived.M8()"); + } + + static void assertMethod(NamedTypeSymbol derived, string methodName, string expectedSignature) + { + var m = derived.GetMember(methodName); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedSignature, m.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -6154,6 +6264,28 @@ protected override void M7(delegate* ptr) {{}} // protected override delegate* M8() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M8").WithLocation(23, 53) ); + + assertMethods(comp.SourceModule); + CompileAndVerify(comp, symbolValidator: assertMethods); + + static void assertMethods(ModuleSymbol module) + { + var derived = module.GlobalNamespace.GetMember("Derived"); + assertMethod(derived, "M1", "void Derived.M1(delegate* ptr)"); + assertMethod(derived, "M2", "delegate* Derived.M2()"); + assertMethod(derived, "M3", "void Derived.M3(delegate* ptr)"); + assertMethod(derived, "M4", "delegate* Derived.M4()"); + assertMethod(derived, "M5", "void Derived.M5(delegate* ptr)"); + assertMethod(derived, "M6", "delegate* Derived.M6()"); + assertMethod(derived, "M7", "void Derived.M7(delegate* ptr)"); + assertMethod(derived, "M8", "delegate* Derived.M8()"); + } + + static void assertMethod(NamedTypeSymbol derived, string methodName, string expectedSignature) + { + var m = derived.GetMember(methodName); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedSignature, m.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] @@ -6161,55 +6293,78 @@ public void Override_NullabilityChanges_PointerByRef() { var comp = CreateCompilationWithFunctionPointers(@" #nullable enable -unsafe class Base -{ - protected virtual void M1(ref delegate* ptr) {{}} - protected virtual ref delegate* M2() => throw null!; - protected virtual void M3(ref delegate* ptr) {{}} - protected virtual ref delegate* M4() => throw null!; - protected virtual void M5(ref delegate* ptr) {{}} - protected virtual ref delegate* M6() => throw null!; - protected virtual void M7(ref delegate* ptr) {{}} - protected virtual ref delegate* M8() => throw null!; +public unsafe class Base +{ + public virtual void M1(ref delegate* ptr) {{}} + public virtual ref delegate* M2() => throw null!; + public virtual void M3(ref delegate* ptr) {{}} + public virtual ref delegate* M4() => throw null!; + public virtual void M5(ref delegate* ptr) {{}} + public virtual ref delegate* M6() => throw null!; + public virtual void M7(ref delegate* ptr) {{}} + public virtual ref delegate* M8() => throw null!; } -unsafe class Derived : Base -{ - protected override void M1(ref delegate* ptr) {{}} - protected override ref delegate* M2() => throw null!; - protected override void M3(ref delegate* ptr) {{}} - protected override ref delegate* M4() => throw null!; - protected override void M5(ref delegate* ptr) {{}} - protected override ref delegate* M6() => throw null!; - protected override void M7(ref delegate* ptr) {{}} - protected override ref delegate* M8() => throw null!; +public unsafe class Derived : Base +{ + public override void M1(ref delegate* ptr) {{}} + public override ref delegate* M2() => throw null!; + public override void M3(ref delegate* ptr) {{}} + public override ref delegate* M4() => throw null!; + public override void M5(ref delegate* ptr) {{}} + public override ref delegate* M6() => throw null!; + public override void M7(ref delegate* ptr) {{}} + public override ref delegate* M8() => throw null!; }"); comp.VerifyDiagnostics( - // (16,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M1(ref delegate* ptr) {{}} - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 29), - // (17,46): warning CS8609: Nullability of reference types in return type doesn't match overridden member. - // protected override ref delegate* M2() => throw null!; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M2").WithLocation(17, 46), - // (18,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M3(ref delegate* ptr) {{}} - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M3").WithArguments("ptr").WithLocation(18, 29), - // (19,52): warning CS8609: Nullability of reference types in return type doesn't match overridden member. - // protected override ref delegate* M4() => throw null!; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M4").WithLocation(19, 52), - // (20,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M5(ref delegate* ptr) {{}} - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("ptr").WithLocation(20, 29), - // (21,47): warning CS8609: Nullability of reference types in return type doesn't match overridden member. - // protected override ref delegate* M6() => throw null!; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 47), - // (22,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M7(ref delegate* ptr) {{}} - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29), - // (23,53): warning CS8609: Nullability of reference types in return type doesn't match overridden member. - // protected override ref delegate* M8() => throw null!; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M8").WithLocation(23, 53) + // (16,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // public override void M1(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 26), + // (17,43): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // public override ref delegate* M2() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M2").WithLocation(17, 43), + // (18,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // public override void M3(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M3").WithArguments("ptr").WithLocation(18, 26), + // (19,49): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // public override ref delegate* M4() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M4").WithLocation(19, 49), + // (20,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // public override void M5(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("ptr").WithLocation(20, 26), + // (21,44): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // public override ref delegate* M6() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 44), + // (22,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. + // public override void M7(ref delegate* ptr) {{}} + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 26), + // (23,50): warning CS8609: Nullability of reference types in return type doesn't match overridden member. + // public override ref delegate* M8() => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M8").WithLocation(23, 50) ); + + assertMethods(comp.SourceModule); + CompileAndVerify(comp, symbolValidator: assertMethods); + + static void assertMethods(ModuleSymbol module) + { + var derived = module.GlobalNamespace.GetMember("Derived"); + assertMethod(derived, "M1", "void Derived.M1(ref delegate* ptr)"); + assertMethod(derived, "M2", "ref delegate* Derived.M2()"); + assertMethod(derived, "M3", "void Derived.M3(ref delegate* ptr)"); + assertMethod(derived, "M4", "ref delegate* Derived.M4()"); + assertMethod(derived, "M5", "void Derived.M5(ref delegate* ptr)"); + assertMethod(derived, "M6", "ref delegate* Derived.M6()"); + assertMethod(derived, "M7", "void Derived.M7(ref delegate* ptr)"); + assertMethod(derived, "M8", "ref delegate* Derived.M8()"); + + } + + static void assertMethod(NamedTypeSymbol derived, string methodName, string expectedSignature) + { + var m = derived.GetMember(methodName); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedSignature, m.ToTestDisplayString(includeNonNullable: true)); + } } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs index 76ba983c416ca..d156d772ed07a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/AccessCheckTests.cs @@ -1502,5 +1502,26 @@ static void Main() // new A(); Diagnostic(ErrorCode.ERR_BadAccess, "A").WithArguments("A").WithLocation(5, 13)); } + + [Fact] + public void FunctionPointerTypesFromOtherInaccessibleAssembly() + { + var comp = CreateCompilation(@" +unsafe class A +{ + internal delegate* ptr1; + internal delegate* ptr2; +}", options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularPreview); + + var ptr1 = comp.GetMember("A.ptr1").Type.GetPublicSymbol(); + var ptr2 = comp.GetMember("A.ptr2").Type.GetPublicSymbol(); + + var comp2 = CreateCompilation("class B {}"); + + var b = comp2.GetMember("B").GetPublicSymbol(); + + Assert.Throws(() => ((Compilation)comp2).IsSymbolAccessibleWithin(ptr1, b)); + Assert.Throws(() => ((Compilation)comp2).IsSymbolAccessibleWithin(ptr2, b)); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs index 878ea86ec968c..55a608df11eb9 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs @@ -646,6 +646,37 @@ static void assertSymbolEquality(Symbol s1, Symbol s2, Equality eq) } } + [Fact] + public void Equality_DifferringRefKinds() + { + var comp = CreateFunctionPointerCompilation(@" +unsafe class C +{ + delegate* ptr1Ref; + delegate* ptr1RefReadonly; + delegate* ptr2Ref; + delegate* ptr2In; + delegate* ptr2Out; +}"); + + var ptr1Ref = comp.GetMember("C.ptr1Ref").Type; + var ptr1RefReadonly = comp.GetMember("C.ptr1RefReadonly").Type; + var ptr2Ref = comp.GetMember("C.ptr2Ref").Type; + var ptr2In = comp.GetMember("C.ptr2In").Type; + var ptr2Out = comp.GetMember("C.ptr2Out").Type; + + var symbolEqualityComparer = new SymbolEqualityComparer( + TypeCompareKind.ConsiderEverything | TypeCompareKind.FunctionPointerRefMatchesOutInRefReadonly | TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds); + Assert.Equal(ptr1Ref.GetPublicSymbol(), ptr1RefReadonly.GetPublicSymbol(), symbolEqualityComparer); + Assert.Equal(ptr2Ref.GetPublicSymbol(), ptr2In.GetPublicSymbol(), symbolEqualityComparer); + Assert.Equal(ptr2Ref.GetPublicSymbol(), ptr2Out.GetPublicSymbol(), symbolEqualityComparer); + Assert.Equal(ptr2In.GetPublicSymbol(), ptr2Out.GetPublicSymbol(), symbolEqualityComparer); + + Assert.Equal(ptr1Ref.GetHashCode(), ptr1RefReadonly.GetHashCode()); + Assert.Equal(ptr2Ref.GetHashCode(), ptr2In.GetHashCode()); + Assert.Equal(ptr2Ref.GetHashCode(), ptr2Out.GetHashCode()); + } + [Fact] public void NoInOutAttribute_NoInOutParameter() { diff --git a/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs b/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs index 891cf5cec2bd3..a6df464ec268a 100644 --- a/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs +++ b/src/Compilers/Core/Portable/Symbols/TypeCompareKind.cs @@ -25,6 +25,14 @@ internal enum TypeCompareKind IgnoreNullableModifiersForReferenceTypes = 8, ObliviousNullableModifierMatchesAny = 16, IgnoreNativeIntegers = 32, + + // For the purposes of a few specific cases such as overload comparisons, we need to consider function pointers that only differ + // by ref, in, out, or ref readonly identical. For these specific scenarios, this option is available. However, it is not in + // in AllIgnoreOptions because except for these few specific cases, ignoring the RefKind is not the correct behavior. + // For overloading, we disallow overloading on just the type of refness in a function pointer parameter or return type. Technically + // we could emit signatures overloaded on this distinction, because we must encode the type of ref in a modreq on the appropriate + // parameter in metadata, which would change the type. However, this would be inconsistent with how ref vs out vs in works on + // top-level signatures today, so we disallow it in source. FunctionPointerRefMatchesOutInRefReadonly = 64, AllNullableIgnoreOptions = IgnoreNullableModifiersForReferenceTypes | ObliviousNullableModifierMatchesAny, From ed27295b02f13b02aaea047648b405e2db98472d Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 19 May 2020 11:06:46 -0700 Subject: [PATCH 09/11] Adjust correspondence in the GetRefKindForHashCode function to match RefKindEquals. --- .../Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs index 0b24fdc4e6751..fbc071dbfc591 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs @@ -175,11 +175,6 @@ internal static bool RefKindEquals(TypeCompareKind compareKind, RefKind refKind1 /// for types that only differ by the type of ref they have. /// internal static RefKind GetRefKindForHashCode(RefKind refKind) - => refKind switch - { - RefKind.Out => RefKind.Ref, - RefKind.In => RefKind.Ref, - _ => refKind - }; + => refKind == RefKind.None ? RefKind.None : RefKind.Ref; } } From 232b02efffc258c7e4badfdf3c805223a097bbac Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 20 May 2020 11:27:25 -0700 Subject: [PATCH 10/11] Adjust tests per feedback. --- .../CodeGen/CodeGenFunctionPointersTests.cs | 87 +++++++++---------- .../Semantics/FunctionPointerTests.cs | 75 +++++++++++++++- .../Semantics/OverloadResolutionTests.cs | 1 - .../Symbols/FunctionPointerTypeSymbolTests.cs | 3 + 4 files changed, 119 insertions(+), 47 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index 6d7658a40edb7..3bc786bd0fba7 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -5861,16 +5861,16 @@ protected override void M1(delegate* cdecl ptr) {} } [Theory] - [InlineData(" ", "ref ")] - [InlineData(" ", "out ")] - [InlineData(" ", "in ")] + [InlineData("", "ref ")] + [InlineData("", "out ")] + [InlineData("", "in ")] [InlineData("ref ", "")] [InlineData("ref ", "out ")] [InlineData("ref ", "in ")] - [InlineData("out ", " ")] + [InlineData("out ", "")] [InlineData("out ", "ref ")] [InlineData("out ", "in ")] - [InlineData("in ", " ")] + [InlineData("in ", "")] [InlineData("in ", "ref ")] [InlineData("in ", "out ")] public void Override_RefnessMustMatch_Parameters(string refKind1, string refKind2) @@ -5890,13 +5890,11 @@ protected override void M1(delegate*<{refKind2}string, void> ptr) {{}} comp.VerifyDiagnostics( // (9,29): error CS0115: 'Derived.M1(delegate*<{refKind2} string, void>)': no suitable method found to override // protected override void M1(delegate*<{refKind2} string, void> ptr) {} - Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments($"Derived.M1(delegate*<{refKindString(refKind2)}string, void>)").WithLocation(9, 29), + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments($"Derived.M1(delegate*<{refKind2}string, void>)").WithLocation(9, 29), // (10,49): error CS0508: 'Derived.M2()': return type must be 'delegate*<{refKind1} string, void>' to match overridden member 'Base.M2()' // protected override delegate*<{refKind2} string, void> M2() => throw null; - Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", $"delegate*<{refKindString(refKind1)}string, void>").WithLocation(10, 48 + refKind2.Length) + Diagnostic(ErrorCode.ERR_CantChangeReturnTypeOnOverride, "M2").WithArguments("Derived.M2()", "Base.M2()", $"delegate*<{refKind1}string, void>").WithLocation(10, 48 + refKind2.Length) ); - - static string refKindString(string orig) => orig == " " ? "" : orig; } [Theory] @@ -6063,7 +6061,7 @@ protected override void M7(delegate* ptr) {} comp.VerifyDiagnostics( ); - assertMethods(comp.SourceModule); + assertMethods(comp.SourceModule); CompileAndVerify(comp, symbolValidator: assertMethods); static void assertMethods(ModuleSymbol module) @@ -6092,24 +6090,24 @@ public void Override_TupleNameChanges() var comp = CreateCompilationWithFunctionPointers(@" unsafe class Base { - protected virtual void M1(delegate*<(int, string)> ptr) {{}} + protected virtual void M1(delegate*<(int, string)> ptr) {} protected virtual delegate*<(int, string)> M2() => throw null; - protected virtual void M3(delegate*<(int, string), void> ptr) {{}} + protected virtual void M3(delegate*<(int, string), void> ptr) {} protected virtual delegate*<(int, string), void> M4() => throw null; - protected virtual void M5(delegate*<(int i, string s)> ptr) {{}} + protected virtual void M5(delegate*<(int i, string s)> ptr) {} protected virtual delegate*<(int i, string s)> M6() => throw null; - protected virtual void M7(delegate*<(int i, string s), void> ptr) {{}} + protected virtual void M7(delegate*<(int i, string s), void> ptr) {} protected virtual delegate*<(int i, string s), void> M8() => throw null; } unsafe class Derived : Base { - protected override void M1(delegate*<(int i, string s)> ptr) {{}} + protected override void M1(delegate*<(int i, string s)> ptr) {} protected override delegate*<(int i, string s)> M2() => throw null; - protected override void M3(delegate*<(int i, string s), void> ptr) {{}} + protected override void M3(delegate*<(int i, string s), void> ptr) {} protected override delegate*<(int i, string s), void> M4() => throw null; - protected override void M5(delegate*<(int, string)> ptr) {{}} + protected override void M5(delegate*<(int, string)> ptr) {} protected override delegate*<(int, string)> M6() => throw null; - protected override void M7(delegate*<(int, string), void> ptr) {{}} + protected override void M7(delegate*<(int, string), void> ptr) {} protected override delegate*<(int, string), void> M8() => throw null; }"); @@ -6151,24 +6149,24 @@ public void Override_NullabilityChanges_NoRefs() #nullable enable unsafe class Base { - protected virtual void M1(delegate* ptr) {{}} + protected virtual void M1(delegate* ptr) {} protected virtual delegate* M2() => throw null!; - protected virtual void M3(delegate* ptr) {{}} + protected virtual void M3(delegate* ptr) {} protected virtual delegate* M4() => throw null!; - protected virtual void M5(delegate* ptr) {{}} + protected virtual void M5(delegate* ptr) {} protected virtual delegate* M6() => throw null!; - protected virtual void M7(delegate* ptr) {{}} + protected virtual void M7(delegate* ptr) {} protected virtual delegate* M8() => throw null!; } unsafe class Derived : Base { - protected override void M1(delegate* ptr) {{}} + protected override void M1(delegate* ptr) {} protected override delegate* M2() => throw null!; - protected override void M3(delegate* ptr) {{}} + protected override void M3(delegate* ptr) {} protected override delegate* M4() => throw null!; - protected override void M5(delegate* ptr) {{}} + protected override void M5(delegate* ptr) {} protected override delegate* M6() => throw null!; - protected override void M7(delegate* ptr) {{}} + protected override void M7(delegate* ptr) {} protected override delegate* M8() => throw null!; }"); @@ -6187,7 +6185,7 @@ protected override void M7(delegate* ptr) {{}} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29) ); - assertMethods(comp.SourceModule); + assertMethods(comp.SourceModule); CompileAndVerify(comp, symbolValidator: assertMethods); static void assertMethods(ModuleSymbol module) @@ -6217,24 +6215,24 @@ public void Override_NullabilityChanges_RefsInParameterReturnTypes() #nullable enable unsafe class Base { - protected virtual void M1(delegate* ptr) {{}} + protected virtual void M1(delegate* ptr) {} protected virtual delegate* M2() => throw null!; - protected virtual void M3(delegate* ptr) {{}} + protected virtual void M3(delegate* ptr) {} protected virtual delegate* M4() => throw null!; - protected virtual void M5(delegate* ptr) {{}} + protected virtual void M5(delegate* ptr) {} protected virtual delegate* M6() => throw null!; - protected virtual void M7(delegate* ptr) {{}} + protected virtual void M7(delegate* ptr) {} protected virtual delegate* M8() => throw null!; } unsafe class Derived : Base { - protected override void M1(delegate* ptr) {{}} + protected override void M1(delegate* ptr) {} protected override delegate* M2() => throw null!; - protected override void M3(delegate* ptr) {{}} + protected override void M3(delegate* ptr) {} protected override delegate* M4() => throw null!; - protected override void M5(delegate* ptr) {{}} + protected override void M5(delegate* ptr) {} protected override delegate* M6() => throw null!; - protected override void M7(delegate* ptr) {{}} + protected override void M7(delegate* ptr) {} protected override delegate* M8() => throw null!; }"); @@ -6265,7 +6263,7 @@ protected override void M7(delegate* ptr) {{}} Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M8").WithLocation(23, 53) ); - assertMethods(comp.SourceModule); + assertMethods(comp.SourceModule); CompileAndVerify(comp, symbolValidator: assertMethods); static void assertMethods(ModuleSymbol module) @@ -6295,24 +6293,24 @@ public void Override_NullabilityChanges_PointerByRef() #nullable enable public unsafe class Base { - public virtual void M1(ref delegate* ptr) {{}} + public virtual void M1(ref delegate* ptr) {} public virtual ref delegate* M2() => throw null!; - public virtual void M3(ref delegate* ptr) {{}} + public virtual void M3(ref delegate* ptr) {} public virtual ref delegate* M4() => throw null!; - public virtual void M5(ref delegate* ptr) {{}} + public virtual void M5(ref delegate* ptr) {} public virtual ref delegate* M6() => throw null!; - public virtual void M7(ref delegate* ptr) {{}} + public virtual void M7(ref delegate* ptr) {} public virtual ref delegate* M8() => throw null!; } public unsafe class Derived : Base { - public override void M1(ref delegate* ptr) {{}} + public override void M1(ref delegate* ptr) {} public override ref delegate* M2() => throw null!; - public override void M3(ref delegate* ptr) {{}} + public override void M3(ref delegate* ptr) {} public override ref delegate* M4() => throw null!; - public override void M5(ref delegate* ptr) {{}} + public override void M5(ref delegate* ptr) {} public override ref delegate* M6() => throw null!; - public override void M7(ref delegate* ptr) {{}} + public override void M7(ref delegate* ptr) {} public override ref delegate* M8() => throw null!; }"); @@ -6370,7 +6368,6 @@ static void assertMethod(NamedTypeSymbol derived, string methodName, string expe [Fact] public void Override_SingleDimensionArraySizesInMetadata() { - var il = @" .class public auto ansi abstract beforefieldinit Base extends [mscorlib]System.Object diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs index 56b853630c4b3..4da3c2c5970ae 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/FunctionPointerTests.cs @@ -1353,7 +1353,7 @@ static void Main() } [Fact] - public void MergeVariantAnnotations() + public void MergeVariantAnnotations_ReturnTypes() { var comp = CreateCompilationWithFunctionPointers(@" #nullable enable @@ -1425,6 +1425,79 @@ void M(bool b) AssertEx.Equal(expectedTypes, invocationTypes); } + [Fact] + public void MergeVariantAnnotations_ParamTypes() + { + var comp = CreateCompilationWithFunctionPointers(@" +#nullable enable +unsafe class C +{ + void M(bool b) + { + delegate* ptr1 = null; + delegate* ptr2 = null; + delegate* ptr3 = null; + delegate* ptr4 = null; + _ = b ? ptr1 : ptr2; + _ = b ? ptr1 : ptr3; + _ = b ? ptr1 : ptr4; + _ = b ? ptr3 : ptr4; + + delegate* ptr5 = null; + delegate* ptr6 = null; + delegate* ptr7 = null; + delegate* ptr8 = null; + _ = b ? ptr5 : ptr6; + _ = b ? ptr5 : ptr7; + _ = b ? ptr5 : ptr8; + _ = b ? ptr7 : ptr8; + } +}"); + + comp.VerifyDiagnostics( + // (12,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' + // _ = b ? ptr1 : ptr3; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr1 : ptr3").WithArguments("delegate*", "delegate*").WithLocation(12, 13), + // (13,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' + // _ = b ? ptr1 : ptr4; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr1 : ptr4").WithArguments("delegate*", "delegate*").WithLocation(13, 13), + // (14,24): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // _ = b ? ptr3 : ptr4; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr4").WithArguments("delegate*", "delegate*").WithLocation(14, 24), + // (21,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' + // _ = b ? ptr5 : ptr7; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr5 : ptr7").WithArguments("delegate*", "delegate*").WithLocation(21, 13), + // (22,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'delegate*' and 'delegate*' + // _ = b ? ptr5 : ptr8; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? ptr5 : ptr8").WithArguments("delegate*", "delegate*").WithLocation(22, 13), + // (23,24): warning CS8619: Nullability of reference types in value of type 'delegate*' doesn't match target type 'delegate*'. + // _ = b ? ptr7 : ptr8; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "ptr8").WithArguments("delegate*", "delegate*").WithLocation(23, 24) + ); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + + var invocationTypes = tree.GetRoot() + .DescendantNodes() + .OfType() + .Select(s => model.GetTypeInfo(s).Type.ToTestDisplayString()) + .ToList(); + + var expectedTypes = new string[] { + "delegate*", + "?", + "?", + "delegate*", + "delegate*", + "?", + "?", + "delegate*" + }; + + AssertEx.Equal(expectedTypes, invocationTypes); + } + [Fact] public void FunctionPointerTypeCannotBeUsedInDynamicTypeArguments() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs index 044947e69e1ff..c2addc5d18049 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs @@ -846,7 +846,6 @@ .method public hidebysig specialname rtspecialname instance void .ctor(class [ms [Fact] public void NormalizeTaskTypes_FunctionPointers() { - string source = @" using System.Runtime.CompilerServices; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs index 55a608df11eb9..881d6cc982deb 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/FunctionPointerTypeSymbolTests.cs @@ -675,6 +675,9 @@ unsafe class C Assert.Equal(ptr1Ref.GetHashCode(), ptr1RefReadonly.GetHashCode()); Assert.Equal(ptr2Ref.GetHashCode(), ptr2In.GetHashCode()); Assert.Equal(ptr2Ref.GetHashCode(), ptr2Out.GetHashCode()); + Assert.Equal(symbolEqualityComparer.GetHashCode(ptr1Ref.GetPublicSymbol()), symbolEqualityComparer.GetHashCode(ptr1RefReadonly.GetPublicSymbol())); + Assert.Equal(symbolEqualityComparer.GetHashCode(ptr2Ref.GetPublicSymbol()), symbolEqualityComparer.GetHashCode(ptr2In.GetPublicSymbol())); + Assert.Equal(symbolEqualityComparer.GetHashCode(ptr2Ref.GetPublicSymbol()), symbolEqualityComparer.GetHashCode(ptr2Out.GetPublicSymbol())); } [Fact] From b8418fb938ff0653364e1397531ed06f76552db6 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 20 May 2020 12:34:13 -0700 Subject: [PATCH 11/11] Fixed formatting. --- .../CodeGen/CodeGenFunctionPointersTests.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index 3bc786bd0fba7..73a2f75ac72ab 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -6008,7 +6008,7 @@ protected override void M7(delegate* ptr) {} comp.VerifyDiagnostics( ); - assertMethods(comp.SourceModule); + assertMethods(comp.SourceModule); CompileAndVerify(comp, symbolValidator: assertMethods); static void assertMethods(ModuleSymbol module) @@ -6172,7 +6172,7 @@ protected override void M7(delegate* ptr) {} comp.VerifyDiagnostics( // (16,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M1(delegate* ptr) {{}} + // protected override void M1(delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 29), // (19,48): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // protected override delegate* M4() => throw null!; @@ -6181,7 +6181,7 @@ protected override void M7(delegate* ptr) {} // protected override delegate* M6() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 43), // (22,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M7(delegate* ptr) {{}} + // protected override void M7(delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29) ); @@ -6238,25 +6238,25 @@ protected override void M7(delegate* ptr) {} comp.VerifyDiagnostics( // (16,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M1(delegate* ptr) {{}} + // protected override void M1(delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 29), // (17,46): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // protected override delegate* M2() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M2").WithLocation(17, 46), // (18,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M3(delegate* ptr) {{}} + // protected override void M3(delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M3").WithArguments("ptr").WithLocation(18, 29), // (19,52): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // protected override delegate* M4() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M4").WithLocation(19, 52), // (20,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M5(delegate* ptr) {{}} + // protected override void M5(delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("ptr").WithLocation(20, 29), // (21,47): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // protected override delegate* M6() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 47), // (22,29): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // protected override void M7(delegate* ptr) {{}} + // protected override void M7(delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 29), // (23,53): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // protected override delegate* M8() => throw null!; @@ -6316,25 +6316,25 @@ public override void M7(ref delegate* ptr) {} comp.VerifyDiagnostics( // (16,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // public override void M1(ref delegate* ptr) {{}} + // public override void M1(ref delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M1").WithArguments("ptr").WithLocation(16, 26), // (17,43): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // public override ref delegate* M2() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M2").WithLocation(17, 43), // (18,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // public override void M3(ref delegate* ptr) {{}} + // public override void M3(ref delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M3").WithArguments("ptr").WithLocation(18, 26), // (19,49): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // public override ref delegate* M4() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M4").WithLocation(19, 49), // (20,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // public override void M5(ref delegate* ptr) {{}} + // public override void M5(ref delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("ptr").WithLocation(20, 26), // (21,44): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // public override ref delegate* M6() => throw null!; Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M6").WithLocation(21, 44), // (22,26): warning CS8610: Nullability of reference types in type of parameter 'ptr' doesn't match overridden member. - // public override void M7(ref delegate* ptr) {{}} + // public override void M7(ref delegate* ptr) {} Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M7").WithArguments("ptr").WithLocation(22, 26), // (23,50): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // public override ref delegate* M8() => throw null!;