Skip to content

Commit

Permalink
Propagate marked attributes from method type parameters to type param…
Browse files Browse the repository at this point in the history
…eters of synthesized types. (#76492)

Related to #73920.
  • Loading branch information
AlekseyTs authored Dec 26, 2024
1 parent cf5eed2 commit e416d99
Show file tree
Hide file tree
Showing 19 changed files with 415 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
if (_factory.CompilationState.Compilation.ShouldEmitNullableAttributes(localFunction))
{
bool constraintsNeedNullableAttribute = typeParameters.Any(
static typeParameter => ((SourceTypeParameterSymbolBase)typeParameter).ConstraintsNeedNullableAttribute());
static typeParameter => ((SourceTypeParameterSymbol)typeParameter).ConstraintsNeedNullableAttribute());

if (constraintsNeedNullableAttribute || hasReturnTypeOrParameter(localFunction, static t => t.NeedsNullableAttribute()))
{
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internal static class ConstraintsHelper
/// generic method. In those cases, additional constraint checks are applied.
/// </summary>
public static TypeParameterBounds ResolveBounds(
this SourceTypeParameterSymbolBase typeParameter,
this SourceTypeParameterSymbol typeParameter,
AssemblySymbol corLibrary,
ConsList<TypeParameterSymbol> inProgress,
ImmutableArray<TypeWithAnnotations> constraintTypes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ private ImmutableArray<SourceMethodTypeParameterSymbol> MakeTypeParameters(Bindi
diagnostics.Add(typeError, location, name, tpEnclosing.ContainingSymbol);
}

var typeParameter = new SourceMethodTypeParameterSymbol(
var typeParameter = new SourceNotOverridingMethodTypeParameterSymbol(
this,
name,
ordinal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ internal sealed class FixedFieldImplementationType : SynthesizedContainer
private readonly FieldSymbol _internalField;

public FixedFieldImplementationType(SourceMemberFieldSymbol field)
: base(GeneratedNames.MakeFixedFieldImplementationName(field.Name), typeParameters: ImmutableArray<TypeParameterSymbol>.Empty, typeMap: TypeMap.Empty)
: base(GeneratedNames.MakeFixedFieldImplementationName(field.Name))
{
_field = field;
_constructor = new SynthesizedInstanceConstructor(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1153,7 +1153,7 @@ private ImmutableArray<TypeParameterSymbol> MakeTypeParameters(MethodDeclaration
ordinal,
locations,
syntaxRefs) :
new SourceMethodTypeParameterSymbol(
new SourceNotOverridingMethodTypeParameterSymbol(
this,
name,
ordinal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
/// <summary>
/// Base class for type and method type parameters.
/// </summary>
internal abstract class SourceTypeParameterSymbolBase : TypeParameterSymbol, IAttributeTargetSymbol
internal abstract class SourceTypeParameterSymbol : TypeParameterSymbol, IAttributeTargetSymbol
{
private readonly ImmutableArray<SyntaxReference> _syntaxRefs;
private readonly ImmutableArray<Location> _locations;
Expand All @@ -31,7 +31,7 @@ internal abstract class SourceTypeParameterSymbolBase : TypeParameterSymbol, IAt
private CustomAttributesBag<CSharpAttributeData> _lazyCustomAttributesBag;
private TypeParameterBounds _lazyBounds = TypeParameterBounds.Unset;

protected SourceTypeParameterSymbolBase(string name, int ordinal, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
protected SourceTypeParameterSymbol(string name, int ordinal, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
{
Debug.Assert(!syntaxRefs.IsEmpty);

Expand Down Expand Up @@ -131,7 +131,7 @@ internal ImmutableArray<SyntaxList<AttributeListSyntax>> MergedAttributeDeclarat
var implementingPart = sourceMethod.SourcePartialImplementation;
if ((object)implementingPart != null)
{
var typeParameter = (SourceTypeParameterSymbolBase)implementingPart.TypeParameters[_ordinal];
var typeParameter = (SourceTypeParameterSymbol)implementingPart.TypeParameters[_ordinal];
mergedAttributesBuilder.AddRange(typeParameter.MergedAttributeDeclarationSyntaxLists);
}
}
Expand Down Expand Up @@ -190,7 +190,7 @@ internal virtual CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
}
else
{
var typeParameter = (SourceTypeParameterSymbolBase)sourceMethod.SourcePartialDefinition.TypeParameters[_ordinal];
var typeParameter = (SourceTypeParameterSymbol)sourceMethod.SourcePartialDefinition.TypeParameters[_ordinal];
CustomAttributesBag<CSharpAttributeData> attributesBag = typeParameter.GetAttributesBag();

lazyAttributesStored = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, attributesBag, null) == null;
Expand Down Expand Up @@ -456,12 +456,12 @@ protected sealed override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownA
}
}

internal sealed class SourceTypeParameterSymbol : SourceTypeParameterSymbolBase
internal sealed class SourceTypeTypeParameterSymbol : SourceTypeParameterSymbol
{
private readonly SourceNamedTypeSymbol _owner;
private readonly VarianceKind _varianceKind;

public SourceTypeParameterSymbol(SourceNamedTypeSymbol owner, string name, int ordinal, VarianceKind varianceKind, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
public SourceTypeTypeParameterSymbol(SourceNamedTypeSymbol owner, string name, int ordinal, VarianceKind varianceKind, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
: base(name, ordinal, locations, syntaxRefs)
{
_owner = owner;
Expand Down Expand Up @@ -602,32 +602,71 @@ private TypeParameterConstraintKind GetConstraintKinds()
}
}

internal sealed class SourceMethodTypeParameterSymbol : SourceTypeParameterSymbolBase
internal abstract class SourceMethodTypeParameterSymbol : SourceTypeParameterSymbol
{
private readonly SourceMethodSymbol _owner;

public SourceMethodTypeParameterSymbol(SourceMethodSymbol owner, string name, int ordinal, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
protected SourceMethodTypeParameterSymbol(string name, int ordinal, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
: base(name, ordinal, locations, syntaxRefs)
{
_owner = owner;
}

internal override void AddDeclarationDiagnostics(BindingDiagnosticBag diagnostics)
=> _owner.AddDeclarationDiagnostics(diagnostics);
public abstract SourceMethodSymbol Owner { get; }

public override TypeParameterKind TypeParameterKind
internal sealed override void AddDeclarationDiagnostics(BindingDiagnosticBag diagnostics)
=> Owner.AddDeclarationDiagnostics(diagnostics);

public sealed override TypeParameterKind TypeParameterKind
{
get
{
return TypeParameterKind.Method;
}
}

public override Symbol ContainingSymbol
public sealed override Symbol ContainingSymbol
{
get { return _owner; }
get { return Owner; }
}

public abstract override bool HasConstructorConstraint { get; }

public abstract override bool HasValueTypeConstraint { get; }

public abstract override bool AllowsRefLikeType { get; }

public abstract override bool IsValueTypeFromConstraintTypes { get; }

public abstract override bool HasReferenceTypeConstraint { get; }

public abstract override bool IsReferenceTypeFromConstraintTypes { get; }

public abstract override bool HasNotNullConstraint { get; }

internal abstract override bool? ReferenceTypeConstraintIsNullable { get; }

internal abstract override bool? IsNotNullable { get; }

public abstract override bool HasUnmanagedTypeConstraint { get; }

protected sealed override ImmutableArray<TypeParameterSymbol> ContainerTypeParameters
{
get { return Owner.TypeParameters; }
}

protected abstract override TypeParameterBounds ResolveBounds(ConsList<TypeParameterSymbol> inProgress, BindingDiagnosticBag diagnostics);
}

internal sealed class SourceNotOverridingMethodTypeParameterSymbol : SourceMethodTypeParameterSymbol
{
private readonly SourceMethodSymbol _owner;

public SourceNotOverridingMethodTypeParameterSymbol(SourceMethodSymbol owner, string name, int ordinal, ImmutableArray<Location> locations, ImmutableArray<SyntaxReference> syntaxRefs)
: base(name, ordinal, locations, syntaxRefs)
{
_owner = owner;
}

public override SourceMethodSymbol Owner => _owner;

public override bool HasConstructorConstraint
{
get
Expand Down Expand Up @@ -722,11 +761,6 @@ public override bool HasUnmanagedTypeConstraint
}
}

protected override ImmutableArray<TypeParameterSymbol> ContainerTypeParameters
{
get { return _owner.TypeParameters; }
}

protected override TypeParameterBounds ResolveBounds(ConsList<TypeParameterSymbol> inProgress, BindingDiagnosticBag diagnostics)
{
var constraints = _owner.GetTypeParameterConstraintTypes();
Expand Down Expand Up @@ -863,7 +897,7 @@ protected override MethodSymbol GetOverriddenMethod(SourceOrdinaryMethodSymbol o
/// <remarks>
/// Exists to copy constraints from the corresponding type parameter of an overridden method.
/// </remarks>
internal sealed class SourceOverridingMethodTypeParameterSymbol : SourceTypeParameterSymbolBase
internal sealed class SourceOverridingMethodTypeParameterSymbol : SourceMethodTypeParameterSymbol
{
private readonly OverriddenMethodTypeParameterMapBase _map;

Expand All @@ -873,24 +907,11 @@ public SourceOverridingMethodTypeParameterSymbol(OverriddenMethodTypeParameterMa
_map = map;
}

public SourceOrdinaryMethodSymbol Owner
public override SourceMethodSymbol Owner
{
get { return _map.OverridingMethod; }
}

public override TypeParameterKind TypeParameterKind
{
get
{
return TypeParameterKind.Method;
}
}

public override Symbol ContainingSymbol
{
get { return this.Owner; }
}

public override bool HasConstructorConstraint
{
get
Expand Down Expand Up @@ -979,11 +1000,6 @@ public override bool HasUnmanagedTypeConstraint
}
}

protected override ImmutableArray<TypeParameterSymbol> ContainerTypeParameters
{
get { return this.Owner.TypeParameters; }
}

protected override TypeParameterBounds ResolveBounds(ConsList<TypeParameterSymbol> inProgress, BindingDiagnosticBag diagnostics)
{
var typeParameter = this.OverriddenTypeParameter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal TypeParameterBuilder(SyntaxReference syntaxRef, SourceNamedTypeSymbol o
internal TypeParameterSymbol MakeSymbol(int ordinal, IList<TypeParameterBuilder> builders, BindingDiagnosticBag diagnostics)
{
var syntaxNode = (TypeParameterSyntax)_syntaxRef.GetSyntax();
var result = new SourceTypeParameterSymbol(
var result = new SourceTypeTypeParameterSymbol(
_owner,
syntaxNode.Identifier.ValueText,
ordinal,
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Symbols/Symbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1668,7 +1668,7 @@ internal void GetCommonNullableValues(CSharpCompilation compilation, ref MostCom
builder.AddValue(((ParameterSymbol)this).TypeWithAnnotations);
break;
case SymbolKind.TypeParameter:
if (this is SourceTypeParameterSymbolBase typeParameter)
if (this is SourceTypeParameterSymbol typeParameter)
{
builder.AddValue(typeParameter.GetSynthesizedNullableAttributeValue());
foreach (var constraintType in typeParameter.ConstraintTypesNoUseSiteDiagnostics)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,13 @@ protected SynthesizedContainer(string name, MethodSymbol containingMethod)
}
}

protected SynthesizedContainer(string name, ImmutableArray<TypeParameterSymbol> typeParameters, TypeMap typeMap)
protected SynthesizedContainer(string name)
{
Debug.Assert(name != null);
Debug.Assert(!typeParameters.IsDefault);
Debug.Assert(typeMap != null);

Name = name;
_typeParameters = typeParameters;
TypeMap = typeMap;
_typeParameters = ImmutableArray<TypeParameterSymbol>.Empty;
TypeMap = TypeMap.Empty;
}

internal TypeMap TypeMap { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ public override bool IsImplicitlyDeclared

internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<CSharpAttributeData> attributes)
{
if (ContainingSymbol.Kind == SymbolKind.NamedType &&
_underlyingTypeParameter.OriginalDefinition is SourceMethodTypeParameterSymbol &&
ContainingSymbol.ContainingModule == _underlyingTypeParameter.OriginalDefinition.ContainingModule)
{
foreach (CSharpAttributeData attr in _underlyingTypeParameter.OriginalDefinition.GetAttributes())
{
if (attr.AttributeClass is { HasCompilerLoweringPreserveAttribute: true })
{
AddSynthesizedAttribute(ref attributes, attr);
}
}
}

base.AddSynthesizedAttributes(moduleBuilder, ref attributes);

if (this.HasUnmanagedTypeConstraint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10557,5 +10557,44 @@ public static async System.Collections.Generic.IAsyncEnumerator<string> Produce(
""";
CompileAndVerify(src, expectedOutput: ExpectedOutput("True one False null"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80).VerifyDiagnostics();
}

[Fact]
public void CompilerLoweringPreserveAttribute_01()
{
string source1 = @"
using System;
using System.Runtime.CompilerServices;
[CompilerLoweringPreserve]
[AttributeUsage(AttributeTargets.GenericParameter)]
public class Preserve1Attribute : Attribute { }
[AttributeUsage(AttributeTargets.GenericParameter)]
public class Preserve2Attribute : Attribute { }
";

string source2 = @"
using System.Collections.Generic;
using System.Threading.Tasks;
class Test1
{
async IAsyncEnumerable<T> M2<[Preserve1][Preserve2]T>(T x)
{
await Task.Yield();
yield return x;
}
}
";
var comp1 = CreateCompilation([source1, source2, CompilerLoweringPreserveAttributeDefinition], targetFramework: TargetFramework.Net80);
CompileAndVerify(comp1, symbolValidator: validate, verify: Verification.FailsPEVerify).VerifyDiagnostics();

static void validate(ModuleSymbol m)
{
AssertEx.SequenceEqual(
["Preserve1Attribute"],
m.GlobalNamespace.GetMember<NamedTypeSymbol>("Test1.<M2>d__0").TypeParameters.Single().GetAttributes().Select(a => a.ToString()));
}
}
}
}
38 changes: 38 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6267,5 +6267,43 @@ .locals init (int V_0,
}
""");
}

[Fact]
public void CompilerLoweringPreserveAttribute_01()
{
string source1 = @"
using System;
using System.Runtime.CompilerServices;
[CompilerLoweringPreserve]
[AttributeUsage(AttributeTargets.GenericParameter)]
public class Preserve1Attribute : Attribute { }
[AttributeUsage(AttributeTargets.GenericParameter)]
public class Preserve2Attribute : Attribute { }
";

string source2 = @"
using System.Threading.Tasks;
class Test1
{
async Task<T> M2<[Preserve1][Preserve2]T>(T x)
{
await Task.Yield();
return x;
}
}
";
var comp1 = CreateCompilation([source1, source2, CompilerLoweringPreserveAttributeDefinition]);
CompileAndVerify(comp1, symbolValidator: validate).VerifyDiagnostics();

static void validate(ModuleSymbol m)
{
AssertEx.SequenceEqual(
["Preserve1Attribute"],
m.GlobalNamespace.GetMember<NamedTypeSymbol>("Test1.<M2>d__0").TypeParameters.Single().GetAttributes().Select(a => a.ToString()));
}
}
}
}
Loading

0 comments on commit e416d99

Please sign in to comment.