Skip to content

Commit

Permalink
Merge pull request #43625 from 333fred/retargeting
Browse files Browse the repository at this point in the history
Support symbol retargeting for function pointers.
  • Loading branch information
msftbot[bot] committed May 2, 2020
2 parents 01db6e0 + 5bbf4c8 commit f339de3
Show file tree
Hide file tree
Showing 3 changed files with 317 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<TypeWithAnnotations>.Empty;
ImmutableArray<ImmutableArray<CustomModifier>> newParamModifiers = default;

var paramCount = signature.ParameterCount;
if (paramCount > 0)
{
var newParameterTypesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(paramCount);
var newParameterCustomModifiersBuilder = ArrayBuilder<ImmutableArray<CustomModifier>>.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.
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -750,6 +751,262 @@ class C1<T>
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*<ref C, ref C, ref C>",
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*<ref C, ref C, ref R>",
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*<ref C, ref C, ref C>",
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*<ref C, ref C, ref C>",
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*<ref R, ref C, ref C>",
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*<ref C, ref C, ref C>",
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*<ref C, ref C, ref C>",
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*<ref C, ref R, ref C>",
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*<ref C, ref C, ref C>",
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*<ref C, ref C, ref C>",
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<RetargetingAssemblySymbol>(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<CustomModifier> 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
Expand Down

0 comments on commit f339de3

Please sign in to comment.