diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs index 8f2d6a15ff509..d477c770fc83e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingSymbolTranslator.cs @@ -729,6 +729,56 @@ public PointerTypeSymbol Retarget(PointerTypeSymbol type) return new PointerTypeSymbol(newPointed); } + public FunctionPointerTypeSymbol Retarget(FunctionPointerTypeSymbol type) + { + var signature = type.Signature; + var newReturn = Retarget(signature.ReturnTypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode); + var newRefModifiers = RetargetModifiers(signature.RefCustomModifiers, out bool symbolModified); + symbolModified = symbolModified || !signature.ReturnTypeWithAnnotations.IsSameAs(newReturn); + + var newParameterTypes = ImmutableArray.Empty; + ImmutableArray> newParamModifiers = default; + + var paramCount = signature.ParameterCount; + if (paramCount > 0) + { + var newParameterTypesBuilder = ArrayBuilder.GetInstance(paramCount); + var newParameterCustomModifiersBuilder = ArrayBuilder>.GetInstance(paramCount); + bool parametersModified = false; + + foreach (var parameter in signature.Parameters) + { + var newParameterType = Retarget(parameter.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode); + var newModifiers = RetargetModifiers(parameter.RefCustomModifiers, out bool customModifiersChanged); + newParameterTypesBuilder.Add(newParameterType); + newParameterCustomModifiersBuilder.Add(newModifiers); + parametersModified = parametersModified || !parameter.TypeWithAnnotations.IsSameAs(newParameterType) || customModifiersChanged; + } + + if (parametersModified) + { + newParameterTypes = newParameterTypesBuilder.ToImmutableAndFree(); + newParamModifiers = newParameterCustomModifiersBuilder.ToImmutableAndFree(); + symbolModified = true; + } + else + { + newParameterTypesBuilder.Free(); + newParameterCustomModifiersBuilder.Free(); + newParameterTypes = signature.ParameterTypesWithAnnotations; + } + } + + if (symbolModified) + { + return type.SubstituteTypeSymbol(newReturn, newParameterTypes, newRefModifiers, newParamModifiers); + } + else + { + return type; + } + } + public static ErrorTypeSymbol Retarget(ErrorTypeSymbol type) { // TODO: if it is a missing symbol error but no longer missing in the target assembly, then we can resolve it here. @@ -1258,6 +1308,11 @@ public override Symbol VisitPointerType(PointerTypeSymbol symbol, RetargetOption return Retarget(symbol); } + public override Symbol VisitFunctionPointerType(FunctionPointerTypeSymbol symbol, RetargetOptions argument) + { + return Retarget(symbol); + } + public override Symbol VisitMethod(MethodSymbol symbol, RetargetOptions options) { return Retarget(symbol); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetCustomModifiers.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetCustomModifiers.cs index 46443ddc82cf9..161e84c9191af 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetCustomModifiers.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetCustomModifiers.cs @@ -202,11 +202,12 @@ void F1(System.DateTime* p) Assert.False(p1.HasExplicitDefaultValue, "Parameter has default value"); Assert.Equal(0, p1.Ordinal); - //PointerTypeSymbol p1Type = (PointerTypeSymbol)p1.Type; + PointerTypeSymbol p1Type = (PointerTypeSymbol)p1.Type; - //Assert.Same(mscorlibAssembly, p1Type.ContainingAssembly); - //Assert.Equal(SpecialType.System_DateTime, p1Type.PointedAtType.SpecialType); - //Assert.Equal(0, p1Type.CustomModifiers.Count); + Assert.Null(p1Type.ContainingAssembly); + Assert.Same(mscorlibAssembly, p1Type.PointedAtType.ContainingAssembly); + Assert.Equal(SpecialType.System_DateTime, p1Type.PointedAtType.SpecialType); + Assert.Equal(0, p1.TypeWithAnnotations.CustomModifiers.Length); } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs index 1544c145b3c63..64ffab31651c8 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; @@ -750,6 +751,262 @@ class C1 Assert.Equal(c1.MangleName, c1r.MangleName); Assert.Equal(c1.MetadataName, c1r.MetadataName); } + + [Fact] + public void FunctionPointerRetargeting_FullyConsistent() + { + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Con]C) *(class [Con]C modopt([Con]C) & modopt([Con]C), class [Con]C modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true)); + } + + [Fact] + public void FunctionPointerRetargeting_Return() + { + TestFunctionPointerRetargetingSignature( + "method class [Ret]R modopt([Con]C) & modopt([Con]C) *(class [Con]C modopt([Con]C) & modopt([Con]C), class [Con]C modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: false, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true)); + + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Ret]R) *(class [Con]C modopt([Con]C) & modopt([Con]C), class [Con]C modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: false, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true)); + + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Ret]R) & modopt([Con]C) *(class [Con]C modopt([Con]C) & modopt([Con]C), class [Con]C modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: false), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true)); + } + + [Fact] + public void FunctionPointerRetargeting_Param1() + { + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Con]C) *(class [Ret]R modopt([Con]C) & modopt([Con]C), class [Con]C modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: false, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true)); + + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Con]C) *(class [Con]C modopt([Con]C) & modopt([Ret]R), class [Con]C modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: false, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true)); + + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Con]C) *(class [Con]C modopt([Ret]R) & modopt([Con]C), class [Con]C modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: false), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true)); + } + + [Fact] + public void FunctionPointerRetargeting_Param2() + { + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Con]C) *(class [Con]C modopt([Con]C) & modopt([Con]C), class [Ret]R modopt([Con]C) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: false, refModConsistent: true, typeModConsistent: true)); + + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Con]C) *(class [Con]C modopt([Con]C) & modopt([Con]C), class [Con]C modopt([Con]C) & modopt([Ret]R))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: false, typeModConsistent: true)); + + TestFunctionPointerRetargetingSignature( + "method class [Con]C modopt([Con]C) & modopt([Con]C) *(class [Con]C modopt([Con]C) & modopt([Con]C), class [Con]C modopt([Ret]R) & modopt([Con]C))", + "delegate*", + returnConsistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param1Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: true), + param2Consistent: (typeConsistent: true, refModConsistent: true, typeModConsistent: false)); + } + + private void TestFunctionPointerRetargetingSignature( + string ilSignature, + string overriddenSignature, + (bool typeConsistent, bool refModConsistent, bool typeModConsistent) returnConsistent, + (bool typeConsistent, bool refModConsistent, bool typeModConsistent) param1Consistent, + (bool typeConsistent, bool refModConsistent, bool typeModConsistent) param2Consistent) + { + var (retargetedAssembly1, retargetedAssembly2, consistentAssembly, originalComp, retargetedComp) = getFunctionPointerRetargetingDefinitions(ilSignature, overriddenSignature); + + var mOriginal = getMethodSymbol(originalComp); + var mRetargeted = getMethodSymbol(retargetedComp); + + Assert.IsType(mRetargeted.ContainingAssembly); + Assert.NotSame(originalComp.Assembly, mRetargeted.ContainingAssembly); + Assert.NotSame(retargetedAssembly1, retargetedAssembly2); + Assert.Same(originalComp.Assembly, ((RetargetingAssemblySymbol)mRetargeted.ContainingAssembly).UnderlyingAssembly); + + var ptrOriginal = (FunctionPointerTypeSymbol)mOriginal.ReturnType; + var ptrRetargeted = (FunctionPointerTypeSymbol)mRetargeted.ReturnType; + + FunctionPointerUtilities.CommonVerifyFunctionPointer(ptrOriginal); + FunctionPointerUtilities.CommonVerifyFunctionPointer(ptrRetargeted); + + if ((true, true, true) == returnConsistent && + (true, true, true) == param1Consistent && + (true, true, true) == param2Consistent) + { + Assert.Same(ptrOriginal, ptrRetargeted); + } + else + { + Assert.NotSame(ptrOriginal, ptrRetargeted); + } + + assert(returnConsistent.typeConsistent, + ptrOriginal.Signature.ReturnType, + ptrRetargeted.Signature.ReturnType); + assert(returnConsistent.refModConsistent, + getModifierTypeSymbol(ptrOriginal.Signature.RefCustomModifiers), + getModifierTypeSymbol(ptrRetargeted.Signature.RefCustomModifiers)); + assert(returnConsistent.typeModConsistent, + getModifierTypeSymbol(ptrOriginal.Signature.ReturnTypeWithAnnotations.CustomModifiers), + getModifierTypeSymbol(ptrRetargeted.Signature.ReturnTypeWithAnnotations.CustomModifiers)); + + Assert.Equal(2, ptrOriginal.Signature.ParameterCount); + Assert.Equal(2, ptrRetargeted.Signature.ParameterCount); + + var param1Original = ptrOriginal.Signature.Parameters[0]; + var param2Original = ptrOriginal.Signature.Parameters[1]; + var param1Retargeted = ptrRetargeted.Signature.Parameters[0]; + var param2Retargeted = ptrRetargeted.Signature.Parameters[1]; + + assert(param1Consistent.typeConsistent, + param1Original.Type, + param1Retargeted.Type); + assert(param1Consistent.refModConsistent, + getModifierTypeSymbol(param1Original.RefCustomModifiers), + getModifierTypeSymbol(param1Retargeted.RefCustomModifiers)); + assert(param1Consistent.typeModConsistent, + getModifierTypeSymbol(param1Original.TypeWithAnnotations.CustomModifiers), + getModifierTypeSymbol(param1Retargeted.TypeWithAnnotations.CustomModifiers)); + + assert(param2Consistent.typeConsistent, + param2Original.Type, + param2Retargeted.Type); + assert(param2Consistent.refModConsistent, + getModifierTypeSymbol(param2Original.RefCustomModifiers), + getModifierTypeSymbol(param2Retargeted.RefCustomModifiers)); + assert(param2Consistent.typeModConsistent, + getModifierTypeSymbol(param2Original.TypeWithAnnotations.CustomModifiers), + getModifierTypeSymbol(param2Retargeted.TypeWithAnnotations.CustomModifiers)); + + static MethodSymbol getMethodSymbol(CSharpCompilation compilation) + { + var c = compilation.GetTypeByMetadataName("Source"); + return c.GetMethod("M"); + } + + static TypeSymbol getModifierTypeSymbol(ImmutableArray modifiers) + => ((CSharpCustomModifier)modifiers.Single()).ModifierSymbol; + + void assert(bool consistent, TypeSymbol originalType, TypeSymbol retargetedType) + { + Assert.False(originalType.IsErrorType()); + Assert.False(retargetedType.IsErrorType()); + if (consistent) + { + Assert.Same(consistentAssembly, originalType.ContainingAssembly); + Assert.Same(consistentAssembly, retargetedType.ContainingAssembly); + } + else + { + Assert.Same(retargetedAssembly1, originalType.ContainingAssembly); + Assert.Same(retargetedAssembly2, retargetedType.ContainingAssembly); + } + } + + static (AssemblySymbol retargetedAssembly1, AssemblySymbol retargetedAssembly2, AssemblySymbol consistentAssembly, CSharpCompilation originalComp, CSharpCompilation retargetedComp) + getFunctionPointerRetargetingDefinitions(string mIlSignature, string mOverriddenSignature) + { + var retargetedSource = @"public class R {{}}"; + var retargetedIdentity = new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true); + var standardReference = TargetFrameworkUtil.StandardReferences.ToArray(); + var retargeted1 = CreateCompilation(retargetedIdentity, new[] { retargetedSource }, references: standardReference); + var retargeted1Ref = retargeted1.ToMetadataReference(); + var retargeted2 = CreateCompilation(retargetedIdentity.WithVersion(new Version(2, 0, 0, 0)), new[] { retargetedSource }, references: standardReference); + var retargeted2Ref = retargeted2.ToMetadataReference(); + + var consistent = CreateCompilation("public class C {}", assemblyName: "Con", targetFramework: TargetFramework.Standard); + var consistentRef = consistent.ToMetadataReference(); + + var ilSource = $@" +{buildAssemblyExternClause(retargeted1)} +{buildAssemblyExternClause(consistent)} +.class public auto ansi beforefieldinit Il + extends [mscorlib]System.Object +{{ + .method public hidebysig newslot virtual + instance {mIlSignature} 'M' () + {{ + .maxstack 8 + + ldnull + throw + }} + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + {{ + .maxstack 8 + ldarg.0 + call instance void [mscorlib]System.Object::.ctor() + ret + }} +}} +"; + + var ilRef = CompileIL(ilSource); + + var originalComp = CreateCompilation($@" +unsafe class Source : Il +{{ + public override {mOverriddenSignature} M() => throw null; +}}", new[] { retargeted1Ref, consistentRef, ilRef }, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.Standard); + + originalComp.VerifyDiagnostics(); + + var retargetedComp = CreateCompilation("", references: new[] { originalComp.ToMetadataReference(), retargeted2Ref, consistentRef, ilRef }, + options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularPreview, + targetFramework: TargetFramework.Standard); + + retargetedComp.VerifyDiagnostics(); + + return (retargeted1.Assembly, retargeted2.Assembly, consistent.Assembly, originalComp, retargetedComp); + + static string buildAssemblyExternClause(CSharpCompilation comp) + { + AssemblyIdentity assemblyIdentity = comp.Assembly.Identity; + System.Version version = assemblyIdentity.Version; + + return $@" +.assembly extern {assemblyIdentity.Name} +{{ + .ver {version.Major}:{version.Minor}:{version.Build}:{version.Revision} +}} +"; + } + } + } } internal abstract class SymbolChecker