Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support symbol retargeting for function pointers. #43625

Merged
3 commits merged into from
May 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
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()
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public [](start = 8, length = 6)

[CompilerTrait(CompilerFeature....)]? And for other new tests #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do this in a separate PR. I haven't added a CompilerFeature flag nor have I done this for any existing tests. I'd want to either do everything or nothing.


In reply to: 418119854 [](ancestors = 418119854)

{
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)
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I had to add an explicit StandardReference above (because that CreateCompilation overload does not add default references), I'm being explicit in the others rather than relying on any change to CreateCompilation to change for all of the calls.

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