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

SymbolMatcher Support #44256

Merged
4 commits merged into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -561,6 +561,48 @@ public override Symbol VisitPointerType(PointerTypeSymbol symbol)
return new PointerTypeSymbol(symbol.PointedAtTypeWithAnnotations.WithTypeAndModifiers(otherPointedAtType, otherModifiers));
}

public override Symbol VisitFunctionPointerType(FunctionPointerTypeSymbol symbol)
{
var sig = symbol.Signature;

var otherReturnType = (TypeSymbol)Visit(sig.ReturnType);
if (otherReturnType is null)
{
return null;
}

var otherRefCustomModifiers = VisitCustomModifiers(sig.RefCustomModifiers);
var otherReturnTypeWithAnnotations = sig.ReturnTypeWithAnnotations.WithTypeAndModifiers(otherReturnType, VisitCustomModifiers(sig.ReturnTypeWithAnnotations.CustomModifiers));

var otherParameterTypes = ImmutableArray<TypeWithAnnotations>.Empty;
ImmutableArray<ImmutableArray<CustomModifier>> otherParamRefCustomModifiers = default;

if (sig.ParameterCount > 0)
{
var otherParamsBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(sig.ParameterCount);
var otherParamRefCustomModifiersBuilder = ArrayBuilder<ImmutableArray<CustomModifier>>.GetInstance(sig.ParameterCount);

foreach (var param in sig.Parameters)
{
var otherType = (TypeSymbol)Visit(param.Type);
if (otherType is null)
{
otherParamsBuilder.Free();
otherParamRefCustomModifiersBuilder.Free();
return null;
}

otherParamRefCustomModifiersBuilder.Add(VisitCustomModifiers(param.RefCustomModifiers));
otherParamsBuilder.Add(param.TypeWithAnnotations.WithTypeAndModifiers(otherType, VisitCustomModifiers(param.TypeWithAnnotations.CustomModifiers)));
}

otherParameterTypes = otherParamsBuilder.ToImmutableAndFree();
otherParamRefCustomModifiers = otherParamRefCustomModifiersBuilder.ToImmutableAndFree();
}

return symbol.SubstituteTypeSymbol(otherReturnTypeWithAnnotations, otherParameterTypes, otherRefCustomModifiers, otherParamRefCustomModifiers);
}

public override Symbol VisitProperty(PropertySymbol symbol)
{
return this.VisitNamedTypeMember(symbol, ArePropertiesEqual);
Expand Down Expand Up @@ -736,6 +778,58 @@ private bool ArePointerTypesEqual(PointerTypeSymbol type, PointerTypeSymbol othe
return AreTypesEqual(type.PointedAtType, other.PointedAtType);
}

private bool AreFunctionPointerTypesEqual(FunctionPointerTypeSymbol type, FunctionPointerTypeSymbol other)
{
var sig = type.Signature;
var otherSig = other.Signature;

ValidateFunctionPointerParamOrReturn(
sig.ReturnTypeWithAnnotations, otherSig.ReturnTypeWithAnnotations,
sig.RefKind, otherSig.RefKind,
sig.RefCustomModifiers, otherSig.RefCustomModifiers,
allowOut: false);
if (sig.RefKind != otherSig.RefKind || !AreTypesEqual(sig.ReturnTypeWithAnnotations, otherSig.ReturnTypeWithAnnotations))
{
return false;
}

return sig.Parameters.SequenceEqual(otherSig.Parameters, AreFunctionPointerParametersEqual);
}

private bool AreFunctionPointerParametersEqual(ParameterSymbol param, ParameterSymbol otherParam)
{
ValidateFunctionPointerParamOrReturn(
param.TypeWithAnnotations, otherParam.TypeWithAnnotations,
param.RefKind, otherParam.RefKind,
param.RefCustomModifiers, otherParam.RefCustomModifiers,
allowOut: false);

return param.RefKind == otherParam.RefKind && AreTypesEqual(param.TypeWithAnnotations, otherParam.TypeWithAnnotations);
}

[Conditional("DEBUG")]
private void ValidateFunctionPointerParamOrReturn(TypeWithAnnotations type, TypeWithAnnotations otherType, RefKind refKind, RefKind otherRefKind, ImmutableArray<CustomModifier> refCustomModifiers, ImmutableArray<CustomModifier> otherRefCustomModifiers, bool allowOut)
333fred marked this conversation as resolved.
Show resolved Hide resolved
333fred marked this conversation as resolved.
Show resolved Hide resolved
{

Debug.Assert(type.CustomModifiers.IsEmpty);
Debug.Assert(otherType.CustomModifiers.IsEmpty);
Debug.Assert(verifyRefModifiers(refCustomModifiers, refKind, allowOut));
Debug.Assert(verifyRefModifiers(otherRefCustomModifiers, otherRefKind, allowOut));

static bool verifyRefModifiers(ImmutableArray<CustomModifier> modifiers, RefKind refKind, bool allowOut)
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
Debug.Assert(RefKind.RefReadOnly == RefKind.In);
switch (refKind)
{
case RefKind.RefReadOnly:
case RefKind.Out when allowOut:
333fred marked this conversation as resolved.
Show resolved Hide resolved
return modifiers.Length == 1;
default:
return modifiers.IsDefaultOrEmpty;
Copy link
Contributor

@AlekseyTs AlekseyTs May 15, 2020

Choose a reason for hiding this comment

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

IsDefaultOrEmpty [](start = 45, length = 16)

Can this be default? #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.

No.


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

}
}
}

private bool ArePropertiesEqual(PropertySymbol property, PropertySymbol other)
{
Debug.Assert(StringOrdinalComparer.Equals(property.MetadataName, other.MetadataName));
Expand Down Expand Up @@ -781,6 +875,9 @@ private bool AreTypesEqual(TypeSymbol type, TypeSymbol other)
case SymbolKind.PointerType:
return ArePointerTypesEqual((PointerTypeSymbol)type, (PointerTypeSymbol)other);

case SymbolKind.FunctionPointer:
return AreFunctionPointerTypesEqual((FunctionPointerTypeSymbol)type, (FunctionPointerTypeSymbol)other);

case SymbolKind.NamedType:
case SymbolKind.ErrorType:
return AreNamedTypesEqual((NamedTypeSymbol)type, (NamedTypeSymbol)other);
Expand Down Expand Up @@ -915,6 +1012,35 @@ public override Symbol VisitPointerType(PointerTypeSymbol symbol)
return new PointerTypeSymbol(symbol.PointedAtTypeWithAnnotations.WithTypeAndModifiers(translatedPointedAtType, translatedModifiers));
}

public override Symbol VisitFunctionPointerType(FunctionPointerTypeSymbol symbol)
{
var sig = symbol.Signature;
var translatedReturnType = (TypeSymbol)Visit(sig.ReturnType);
var translatedReturnTypeWithAnnotations = sig.ReturnTypeWithAnnotations.WithTypeAndModifiers(translatedReturnType, VisitCustomModifiers(sig.ReturnTypeWithAnnotations.CustomModifiers));
var translatedRefCustomModifiers = VisitCustomModifiers(sig.RefCustomModifiers);

var translatedParameterTypes = ImmutableArray<TypeWithAnnotations>.Empty;
ImmutableArray<ImmutableArray<CustomModifier>> translatedParamRefCustomModifiers = default;

if (sig.ParameterCount > 0)
{
var translatedParamsBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(sig.ParameterCount);
var translatedParamRefCustomModifiersBuilder = ArrayBuilder<ImmutableArray<CustomModifier>>.GetInstance(sig.ParameterCount);

foreach (var param in sig.Parameters)
{
var translatedParamType = (TypeSymbol)Visit(param.Type);
translatedParamsBuilder.Add(param.TypeWithAnnotations.WithTypeAndModifiers(translatedParamType, VisitCustomModifiers(param.TypeWithAnnotations.CustomModifiers)));
translatedParamRefCustomModifiersBuilder.Add(VisitCustomModifiers(param.RefCustomModifiers));
}

translatedParameterTypes = translatedParamsBuilder.ToImmutableAndFree();
translatedParamRefCustomModifiers = translatedParamRefCustomModifiersBuilder.ToImmutableAndFree();
}

return symbol.SubstituteTypeSymbol(translatedReturnTypeWithAnnotations, translatedParameterTypes, translatedRefCustomModifiers, translatedParamRefCustomModifiers);
}

public override Symbol VisitTypeParameter(TypeParameterSymbol symbol)
{
return symbol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1272,5 +1272,135 @@ event Action F { add { } remove { } }
Assert.Same(e0, matcher.MapDefinition(e1));
Assert.Same(f0, matcher.MapDefinition(f1));
}

[Fact]
public void FunctionPointerMembersTranslated()
{
var source = @"
unsafe class C
{
delegate*<void> f1;
delegate*<C, C, C> f2;
delegate*<ref C> f3;
delegate*<ref readonly C> f4;
delegate*<ref C, void> f5;
delegate*<in C, void> f6;
delegate*<out C, void> f7;
}
";

var compilation0 = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview);
var compilation1 = compilation0.WithSource(source);

var matcher = new CSharpSymbolMatcher(
null,
compilation1.SourceAssembly,
default,
compilation0.SourceAssembly,
default,
null);

for (int i = 1; i <= 7; i++)
{
var f_0 = compilation0.GetMember<FieldSymbol>($"C.f{i}");
var f_1 = compilation1.GetMember<FieldSymbol>($"C.f{i}");

Assert.Same(f_0, matcher.MapDefinition(f_1));
}
}

[Theory]
[InlineData("C", "void")]
[InlineData("C", "object")]
[InlineData("C", "ref C")]
[InlineData("C", "ref readonly C")]
[InlineData("ref C", "ref readonly C")]
public void FunctionPointerMembers_ReturnMismatch(string return1, string return2)
{
var source1 = $@"
unsafe class C
{{
delegate*<C, {return1}> f1;
}}";

var source2 = $@"
unsafe class C
{{
delegate*<C, {return2}> f1;
}}";

var compilation0 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview);
var compilation1 = compilation0.WithSource(source2);

var matcher = new CSharpSymbolMatcher(
null,
compilation1.SourceAssembly,
default,
compilation0.SourceAssembly,
default,
null);

var f_1 = compilation1.GetMember<FieldSymbol>($"C.f1");

Assert.Null(matcher.MapDefinition(f_1));
}

[Theory]
[InlineData("C", "object")]
[InlineData("C", "ref C")]
[InlineData("C", "out C")]
[InlineData("C", "in C")]
[InlineData("ref C", "out C")]
[InlineData("ref C", "in C")]
[InlineData("out C", "in C")]
[InlineData("C, C", "C")]
public void FunctionPointerMembers_ParamMismatch(string param1, string param2)
{
var source1 = $@"
unsafe class C
{{
delegate*<{param1}, C, void>* f1;
}}";

var source2 = $@"
unsafe class C
{{
delegate*<{param2}, C, void>* f1;
}}";

verify(source1, source2);

source1 = $@"
unsafe class C
{{
delegate*<C, {param1}, void> f1;
}}";

source2 = $@"
unsafe class C
{{
delegate*<C, {param2}, void> f1;
}}";

verify(source1, source2);

static void verify(string source1, string source2)
{
var compilation0 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview);
var compilation1 = compilation0.WithSource(source2);

var matcher = new CSharpSymbolMatcher(
null,
compilation1.SourceAssembly,
default,
compilation0.SourceAssembly,
default,
null);

var f_1 = compilation1.GetMember<FieldSymbol>($"C.f1");

Assert.Null(matcher.MapDefinition(f_1));
}
}
}
}