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

Local function attributes emit #39226

Merged
Merged
Show file tree
Hide file tree
Changes from 9 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 @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp
internal sealed class SynthesizedClosureMethod : SynthesizedMethodBaseSymbol, ISynthesizedMethodBodyImplementationSymbol
{
private readonly MethodSymbol _topLevelMethod;
private readonly MethodSymbol _originalMethod;
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
private readonly ImmutableArray<NamedTypeSymbol> _structEnvironments;

internal readonly DebugId LambdaId;
Expand All @@ -39,6 +40,7 @@ originalMethod is LocalFunctionSymbol
MakeDeclarationModifiers(closureKind, originalMethod))
{
_topLevelMethod = topLevelMethod;
_originalMethod = originalMethod;
ClosureKind = closureKind;
LambdaId = lambdaId;

Expand Down Expand Up @@ -163,6 +165,10 @@ protected override ImmutableArray<TypeSymbol> ExtraSynthesizedRefParameters
internal override bool IsExpressionBodied => false;
internal MethodSymbol TopLevelMethod => _topLevelMethod;

public override ImmutableArray<CSharpAttributeData> GetAttributes() => _originalMethod.GetAttributes();
Copy link
Member

@agocke agocke Oct 23, 2019

Choose a reason for hiding this comment

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

Hmm, I wonder if we should just override this for CCI. Maybe this should throw Unreachable? Who is asking for the attributes on a synthesized symbol? #Resolved

Copy link
Contributor Author

@RikkiGibson RikkiGibson Nov 12, 2019

Choose a reason for hiding this comment

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

This is used when emitting the synthesized symbol's attributes. #Resolved

RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved

public override ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes() => _originalMethod.GetReturnTypeAttributes();

internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree)
{
// Syntax offset of a syntax node contained in a lambda body is calculated by the containing top-level method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,13 @@ private ImmutableArray<ParameterSymbol> MakeParameters()
var parameters = this.BaseMethodParameters;
foreach (var p in parameters)
{
builder.Add(SynthesizedParameterSymbol.Create(this, this.TypeMap.SubstituteType(p.OriginalDefinition.TypeWithAnnotations), ordinal++, p.RefKind, p.Name));
builder.Add(SynthesizedParameterSymbol.Create(
this,
this.TypeMap.SubstituteType(p.OriginalDefinition.TypeWithAnnotations),
ordinal++,
p.RefKind,
p.Name,
attributes: p.GetAttributes()));
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
}
var extraSynthed = ExtraSynthesizedRefParameters;
if (!extraSynthed.IsDefaultOrEmpty)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,7 @@ private CustomAttributesBag<CSharpAttributeData> GetAttributesBag(ref CustomAttr
/// Gets the attributes applied on this symbol.
/// Returns an empty array if there are no attributes.
/// </summary>
public sealed override ImmutableArray<CSharpAttributeData> GetAttributes()
public override ImmutableArray<CSharpAttributeData> GetAttributes()
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
{
return this.GetAttributesBag().Attributes;
}
Expand All @@ -1063,7 +1063,7 @@ public sealed override ImmutableArray<CSharpAttributeData> GetAttributes()
/// Gets the attributes applied on the return value of this method symbol.
/// Returns an empty array if there are no attributes.
/// </summary>
public sealed override ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes()
public override ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes()
{
return this.GetReturnTypeAttributesBag().Attributes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,15 @@ public static ParameterSymbol Create(
int ordinal,
RefKind refKind,
string name = "",
ImmutableArray<CustomModifier> refCustomModifiers = default(ImmutableArray<CustomModifier>))
ImmutableArray<CustomModifier> refCustomModifiers = default,
ImmutableArray<CSharpAttributeData> attributes = default)
{
if (refCustomModifiers.IsDefaultOrEmpty)
if (refCustomModifiers.IsDefaultOrEmpty && attributes.IsDefaultOrEmpty)
{
return new SynthesizedParameterSymbol(container, type, ordinal, refKind, name);
}

return new SynthesizedParameterSymbolWithCustomModifiers(container, type, ordinal, refKind, name, refCustomModifiers);
return new SynthesizedParameterSymbolWithCustomModifiers(container, type, ordinal, refKind, name, refCustomModifiers, attributes.NullToEmpty());
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
Expand Down Expand Up @@ -231,23 +232,31 @@ public override ImmutableArray<CustomModifier> RefCustomModifiers
private sealed class SynthesizedParameterSymbolWithCustomModifiers : SynthesizedParameterSymbolBase
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly ImmutableArray<CustomModifier> _refCustomModifiers;
private readonly ImmutableArray<CSharpAttributeData> _attributes;

public SynthesizedParameterSymbolWithCustomModifiers(
MethodSymbol container,
TypeWithAnnotations type,
int ordinal,
RefKind refKind,
string name,
ImmutableArray<CustomModifier> refCustomModifiers)
ImmutableArray<CustomModifier> refCustomModifiers,
ImmutableArray<CSharpAttributeData> attributes)
: base(container, type, ordinal, refKind, name)
{
_refCustomModifiers = refCustomModifiers.NullToEmpty();
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
_attributes = attributes;
}

public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get { return _refCustomModifiers; }
}

public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
return _attributes;
}
}
}
}
228 changes: 228 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLocalFunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5156,6 +5156,234 @@ .locals init (Program.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0
");
}

[Fact]
public void LocalFunctionAttribute()
{
var source = @"
class A : System.Attribute { }

class C
{
public void M()
{
[A]
void local1()
{
}

[return: A]
void local2()
{
}

void local3([A] int i)
{
}

void local4<[A] T>()
{
}
}
}
";
CompileAndVerify(
source,
options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All),
parseOptions: TestOptions.RegularPreview,
symbolValidator: validate);

void validate(ModuleSymbol module)
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
{
var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var aAttribute = module.GlobalNamespace.GetMember<NamedTypeSymbol>("A");

var localFn1 = cClass.GetMethod("<M>g__local1|0_0");
var attrs1 = localFn1.GetAttributes();
Assert.Equal(
expected: new[]
{
module.CorLibrary().GetTypeByMetadataName("System.Runtime.CompilerServices.CompilerGeneratedAttribute"),
aAttribute
},
actual: attrs1.Select(a => a.AttributeClass));
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved

var localFn2 = cClass.GetMethod("<M>g__local2|0_1");
var attrs2 = localFn2.GetReturnTypeAttributes();
Assert.Equal(aAttribute, attrs2.Single().AttributeClass);

var localFn3 = cClass.GetMethod("<M>g__local3|0_2");
var attrs3 = localFn3.GetParameters().Single().GetAttributes();
Assert.Equal(aAttribute, attrs3.Single().AttributeClass);

var localFn4 = cClass.GetMethod("<M>g__local4|0_3");
var attrs4 = localFn4.TypeParameters.Single().GetAttributes();
Assert.Equal(aAttribute, attrs4.Single().AttributeClass);
}
}

[Fact]
public void LocalFunctionAttribute_Complex()
{
var source = @"
class A1 : System.Attribute { }
class A2 : System.Attribute { internal A2(int i, string s) { } }
class A3 : System.Attribute { internal A3(params int[] values) { } }

class C
{
public void M()
{
[A1, A2(1, ""hello"")]
[A3(1, 2, 3, 4, 5)]
void local1()
{
}
}
}
";
CompileAndVerify(
source,
options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All),
parseOptions: TestOptions.RegularPreview,
symbolValidator: validate);

void validate(ModuleSymbol module)
{
var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var attr1 = module.GlobalNamespace.GetMember<NamedTypeSymbol>("A1");
var attr2 = module.GlobalNamespace.GetMember<NamedTypeSymbol>("A2");
var attr3 = module.GlobalNamespace.GetMember<NamedTypeSymbol>("A3");

var localFn1 = cClass.GetMethod("<M>g__local1|0_0");
var attrs = localFn1.GetAttributes();
Assert.Equal(
expected: new[]
{
module.CorLibrary().GetTypeByMetadataName("System.Runtime.CompilerServices.CompilerGeneratedAttribute"),
attr1,
attr2,
attr3
},
actual: attrs.Select(a => a.AttributeClass));

Assert.Empty(attrs[0].ConstructorArguments);
Assert.Empty(attrs[1].ConstructorArguments);
Assert.Equal(new object[] { 1, "hello" }, attrs[2].ConstructorArguments.Select(a => a.Value));

var attr3Args = attrs[3].ConstructorArguments.Single().Values;
Assert.Equal(new object[] { 1, 2, 3, 4, 5 }, attr3Args.Select(a => a.Value));
}
}

[Fact]
public void LocalFunctionAttributeArgument()
{
var source = @"
class A : System.Attribute { internal A(int i) { } }

class C
{
public void M()
{
[A(42)]
void local1()
{
}
}
}
";
var verifier = CompileAndVerify(
source,
options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All),
parseOptions: TestOptions.RegularPreview,
symbolValidator: validate);

void validate(ModuleSymbol module)
{
var cClass = module.GlobalNamespace.GetMember<NamedTypeSymbol>("C");
var aAttribute = module.GlobalNamespace.GetMember<NamedTypeSymbol>("A");

var localFn1 = cClass.GetMethod("<M>g__local1|0_0");
var attrs1 = localFn1.GetAttributes();
Assert.Equal(
expected: new[]
{
module.CorLibrary().GetTypeByMetadataName("System.Runtime.CompilerServices.CompilerGeneratedAttribute"),
aAttribute
},
actual: attrs1.Select(a => a.AttributeClass));

var arg = attrs1[1].ConstructorArguments.Single();
Assert.Equal(42, arg.Value);
}
}

[ConditionalFact(typeof(DesktopOnly))]
public void LocalFunctionAttribute_TypeIL()
{
var source = @"
class A : System.Attribute { internal A(int i) { } }

class C
{
public void M()
{
[A(42)]
void local1()
{
}
}
}
";
var verifier = CompileAndVerify(
source,
options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All),
parseOptions: TestOptions.RegularPreview);

verifier.VerifyTypeIL("C", @"
.class private auto ansi beforefieldinit C
extends [mscorlib]System.Object
{
// Methods
.method public hidebysig
instance void M () cil managed
{
// Method begins at RVA 0x205a
// Code size 3 (0x3)
.maxstack 8
IL_0000: nop
IL_0001: nop
IL_0002: ret
} // end of method C::M
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x205e
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method C::.ctor
.method assembly hidebysig static
void '<M>g__local1|0_0' () cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
.custom instance void A::.ctor(int32) = (
01 00 2a 00 00 00 00 00
)
// Method begins at RVA 0x2067
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method C::'<M>g__local1|0_0'
} // end of class C");
}

internal CompilationVerifier VerifyOutput(string source, string output, CSharpCompilationOptions options, Verification verify = Verification.Passes)
{
var comp = CreateCompilationWithMscorlib45AndCSharp(source, options: options);
Expand Down