From e416d999af7f2658ed976b614fcb63b19a8137b4 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 26 Dec 2024 09:44:43 -0800 Subject: [PATCH] Propagate marked attributes from method type parameters to type parameters of synthesized types. (#76492) Related to #73920. --- .../Lowering/LocalRewriter/LocalRewriter.cs | 2 +- .../Portable/Symbols/ConstraintsHelper.cs | 2 +- .../Symbols/Source/LocalFunctionSymbol.cs | 2 +- .../Symbols/Source/SourceFixedFieldSymbol.cs | 2 +- .../Source/SourceOrdinaryMethodSymbol.cs | 2 +- .../Source/SourceTypeParameterSymbol.cs | 98 +++++++++------- .../Symbols/Source/TypeParameterBuilder.cs | 2 +- .../CSharp/Portable/Symbols/Symbol.cs | 2 +- .../Synthesized/SynthesizedContainer.cs | 8 +- ...nthesizedSubstitutedTypeParameterSymbol.cs | 13 +++ .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 39 +++++++ .../Test/Emit/CodeGen/CodeGenAsyncTests.cs | 38 +++++++ ...odeGenMethodGroupConversionCachingTests.cs | 41 +++++++ .../Test/Semantic/Semantics/DynamicTests.cs | 37 ++++++ .../Test/Semantic/Semantics/IteratorTests.cs | 37 ++++++ .../Test/Semantic/Semantics/LambdaTests.cs | 35 ++++++ .../Symbol/DocumentationComments/CrefTests.cs | 2 +- .../Test/Symbol/Symbols/LocalFunctionTests.cs | 106 ++++++++++++++++++ .../Symbols/Retargeting/RetargetingTests.cs | 2 +- 19 files changed, 415 insertions(+), 55 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 9a81320dfef8c..8e73de6531706 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -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())) { diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 276d898f8a944..ab7001379184a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -69,7 +69,7 @@ internal static class ConstraintsHelper /// generic method. In those cases, additional constraint checks are applied. /// public static TypeParameterBounds ResolveBounds( - this SourceTypeParameterSymbolBase typeParameter, + this SourceTypeParameterSymbol typeParameter, AssemblySymbol corLibrary, ConsList inProgress, ImmutableArray constraintTypes, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 42e505bfe480d..81beddf885f99 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -452,7 +452,7 @@ private ImmutableArray MakeTypeParameters(Bindi diagnostics.Add(typeError, location, name, tpEnclosing.ContainingSymbol); } - var typeParameter = new SourceMethodTypeParameterSymbol( + var typeParameter = new SourceNotOverridingMethodTypeParameterSymbol( this, name, ordinal, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs index 3f3943bc2c9b2..16402ae80cc81 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFixedFieldSymbol.cs @@ -153,7 +153,7 @@ internal sealed class FixedFieldImplementationType : SynthesizedContainer private readonly FieldSymbol _internalField; public FixedFieldImplementationType(SourceMemberFieldSymbol field) - : base(GeneratedNames.MakeFixedFieldImplementationName(field.Name), typeParameters: ImmutableArray.Empty, typeMap: TypeMap.Empty) + : base(GeneratedNames.MakeFixedFieldImplementationName(field.Name)) { _field = field; _constructor = new SynthesizedInstanceConstructor(this); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index 2b14c397e27bf..c0df4ffcad913 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -1153,7 +1153,7 @@ private ImmutableArray MakeTypeParameters(MethodDeclaration ordinal, locations, syntaxRefs) : - new SourceMethodTypeParameterSymbol( + new SourceNotOverridingMethodTypeParameterSymbol( this, name, ordinal, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index c402d52b987f4..76abe90229d18 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// /// Base class for type and method type parameters. /// - internal abstract class SourceTypeParameterSymbolBase : TypeParameterSymbol, IAttributeTargetSymbol + internal abstract class SourceTypeParameterSymbol : TypeParameterSymbol, IAttributeTargetSymbol { private readonly ImmutableArray _syntaxRefs; private readonly ImmutableArray _locations; @@ -31,7 +31,7 @@ internal abstract class SourceTypeParameterSymbolBase : TypeParameterSymbol, IAt private CustomAttributesBag _lazyCustomAttributesBag; private TypeParameterBounds _lazyBounds = TypeParameterBounds.Unset; - protected SourceTypeParameterSymbolBase(string name, int ordinal, ImmutableArray locations, ImmutableArray syntaxRefs) + protected SourceTypeParameterSymbol(string name, int ordinal, ImmutableArray locations, ImmutableArray syntaxRefs) { Debug.Assert(!syntaxRefs.IsEmpty); @@ -131,7 +131,7 @@ internal ImmutableArray> 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); } } @@ -190,7 +190,7 @@ internal virtual CustomAttributesBag GetAttributesBag() } else { - var typeParameter = (SourceTypeParameterSymbolBase)sourceMethod.SourcePartialDefinition.TypeParameters[_ordinal]; + var typeParameter = (SourceTypeParameterSymbol)sourceMethod.SourcePartialDefinition.TypeParameters[_ordinal]; CustomAttributesBag attributesBag = typeParameter.GetAttributesBag(); lazyAttributesStored = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, attributesBag, null) == null; @@ -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 locations, ImmutableArray syntaxRefs) + public SourceTypeTypeParameterSymbol(SourceNamedTypeSymbol owner, string name, int ordinal, VarianceKind varianceKind, ImmutableArray locations, ImmutableArray syntaxRefs) : base(name, ordinal, locations, syntaxRefs) { _owner = owner; @@ -602,20 +602,19 @@ 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 locations, ImmutableArray syntaxRefs) + protected SourceMethodTypeParameterSymbol(string name, int ordinal, ImmutableArray locations, ImmutableArray 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 { @@ -623,11 +622,51 @@ public override TypeParameterKind TypeParameterKind } } - 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 ContainerTypeParameters + { + get { return Owner.TypeParameters; } + } + + protected abstract override TypeParameterBounds ResolveBounds(ConsList inProgress, BindingDiagnosticBag diagnostics); + } + + internal sealed class SourceNotOverridingMethodTypeParameterSymbol : SourceMethodTypeParameterSymbol + { + private readonly SourceMethodSymbol _owner; + + public SourceNotOverridingMethodTypeParameterSymbol(SourceMethodSymbol owner, string name, int ordinal, ImmutableArray locations, ImmutableArray syntaxRefs) + : base(name, ordinal, locations, syntaxRefs) + { + _owner = owner; } + public override SourceMethodSymbol Owner => _owner; + public override bool HasConstructorConstraint { get @@ -722,11 +761,6 @@ public override bool HasUnmanagedTypeConstraint } } - protected override ImmutableArray ContainerTypeParameters - { - get { return _owner.TypeParameters; } - } - protected override TypeParameterBounds ResolveBounds(ConsList inProgress, BindingDiagnosticBag diagnostics) { var constraints = _owner.GetTypeParameterConstraintTypes(); @@ -863,7 +897,7 @@ protected override MethodSymbol GetOverriddenMethod(SourceOrdinaryMethodSymbol o /// /// Exists to copy constraints from the corresponding type parameter of an overridden method. /// - internal sealed class SourceOverridingMethodTypeParameterSymbol : SourceTypeParameterSymbolBase + internal sealed class SourceOverridingMethodTypeParameterSymbol : SourceMethodTypeParameterSymbol { private readonly OverriddenMethodTypeParameterMapBase _map; @@ -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 @@ -979,11 +1000,6 @@ public override bool HasUnmanagedTypeConstraint } } - protected override ImmutableArray ContainerTypeParameters - { - get { return this.Owner.TypeParameters; } - } - protected override TypeParameterBounds ResolveBounds(ConsList inProgress, BindingDiagnosticBag diagnostics) { var typeParameter = this.OverriddenTypeParameter; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterBuilder.cs b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterBuilder.cs index c76e5abe74503..9c262cf2c23a5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterBuilder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterBuilder.cs @@ -34,7 +34,7 @@ internal TypeParameterBuilder(SyntaxReference syntaxRef, SourceNamedTypeSymbol o internal TypeParameterSymbol MakeSymbol(int ordinal, IList builders, BindingDiagnosticBag diagnostics) { var syntaxNode = (TypeParameterSyntax)_syntaxRef.GetSyntax(); - var result = new SourceTypeParameterSymbol( + var result = new SourceTypeTypeParameterSymbol( _owner, syntaxNode.Identifier.ValueText, ordinal, diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 8aa95a4dfdf11..216b61f6d68ae 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -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) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs index 1ebb62c711944..f6e73c27ebbe0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs @@ -38,15 +38,13 @@ protected SynthesizedContainer(string name, MethodSymbol containingMethod) } } - protected SynthesizedContainer(string name, ImmutableArray 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.Empty; + TypeMap = TypeMap.Empty; } internal TypeMap TypeMap { get; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs index 29d619d53c3a4..01b96437e8bf4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSubstitutedTypeParameterSymbol.cs @@ -34,6 +34,19 @@ public override bool IsImplicitlyDeclared internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder 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) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index c5cb4eeb9a3eb..d53a9fe93a818 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -10557,5 +10557,44 @@ public static async System.Collections.Generic.IAsyncEnumerator 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 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("Test1.d__0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 89482c2907c11..40f0f999c2904 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -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 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("Test1.d__0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 891ff106afb8e..877c8042eb162 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -6349,6 +6349,47 @@ .locals init (C1.F V_0) //x "); } + [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 = @" +class Test1 +{ + System.Action M2<[Preserve1][Preserve2]T>() + { + return (System.Action)D.Target; + } +} + +class D +{ + public static void Target() { } +} + +"; + var comp1 = CreateCompilation([source1, source2, CompilerLoweringPreserveAttributeDefinition]); + CompileAndVerify(comp1, symbolValidator: validate).VerifyDiagnostics(); + + static void validate(ModuleSymbol m) + { + AssertEx.SequenceEqual( + ["Preserve1Attribute"], + m.GlobalNamespace.GetMember("Test1.O__0_0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } + private static Action VerifyCacheContainer(string typeName, int arity, params string[] expectedFields) { return module => diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs index 017ab0d3388b0..8b4dc57bdf9b9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs @@ -11694,5 +11694,42 @@ static void Main() CompileAndVerify(comp1, expectedOutput: "123").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 = @" +class Test1 +{ + dynamic M2<[Preserve1][Preserve2]T>(T x, dynamic y) + { + return y.M(x); + } +} +"; + var comp1 = CreateCompilation( + [source1, source2, CompilerLoweringPreserveAttributeDefinition], + targetFramework: TargetFramework.StandardAndCSharp); + CompileAndVerify(comp1, symbolValidator: validate).VerifyDiagnostics(); + + static void validate(ModuleSymbol m) + { + AssertEx.SequenceEqual( + ["Preserve1Attribute"], + m.GlobalNamespace.GetMember("Test1.<>o__0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs index 170096bd82075..173b63c6dbb9c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs @@ -661,5 +661,42 @@ public IEnumerator> GetEnumerator(KeyValuePair..ctor(TKey key, TValue value)", symbolInfo.CandidateSymbols.Select(c => c.ToTestDisplayString())); Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); } + + [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; + +class Test1 +{ + IEnumerable M2<[Preserve1][Preserve2]T>(T x) + { + 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("Test1.d__0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index fd8620a66a1e8..2916df77e1f08 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -8553,5 +8553,40 @@ public void ParamsArray_ThisModifier_02() // var lam = (params this int[] xs) => xs.Length; Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(1, 19)); } + + [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 = @" +class Test1 +{ + System.Func M2<[Preserve1][Preserve2]T>(T x) + { + return () => x; + } +} +"; + var comp1 = CreateCompilation([source1, source2, CompilerLoweringPreserveAttributeDefinition]); + CompileAndVerify(comp1, symbolValidator: validate).VerifyDiagnostics(); + + static void validate(ModuleSymbol m) + { + AssertEx.SequenceEqual( + ["Preserve1Attribute"], + m.GlobalNamespace.GetMember("Test1.<>c__DisplayClass0_0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs index 68e18c40b7ee3..660bdec42dad4 100644 --- a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/CrefTests.cs @@ -5564,7 +5564,7 @@ class C Assert.IsType(crefTypeParam.GetSymbol()); var sourceTypeParam = referencedType.TypeParameters.Single(); - Assert.IsType(sourceTypeParam.GetSymbol()); + Assert.IsType(sourceTypeParam.GetSymbol()); Assert.NotEqual(crefTypeParam, sourceTypeParam); Assert.NotEqual(sourceTypeParam, crefTypeParam); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs index fc44f296c1a3d..f2ef5f74c6c38 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs @@ -524,5 +524,111 @@ public static void M() { } Assert.Equal("void C.M()", symbol.ToTestDisplayString()); Assert.Equal("C", symbol.ContainingSymbol.ToTestDisplayString()); } + + [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 = @" +class Test1 +{ + System.Func M2<[Preserve1][Preserve2]T>(T x) + { + return local; + T local() => x; + } +} +"; + var comp1 = CreateCompilation([source1, source2, CompilerLoweringPreserveAttributeDefinition]); + CompileAndVerify(comp1, symbolValidator: validate).VerifyDiagnostics(); + + static void validate(ModuleSymbol m) + { + AssertEx.SequenceEqual( + ["Preserve1Attribute"], + m.GlobalNamespace.GetMember("Test1.<>c__DisplayClass0_0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } + + [Fact] + public void CompilerLoweringPreserveAttribute_02() + { + string source1 = @" +using System; + +[AttributeUsage(AttributeTargets.GenericParameter)] +public class Preserve1Attribute : Attribute { } +"; + + string source2 = @" +class Test1 +{ + System.Func M2() + { + return local; + T local<[Preserve1]T>(T x) => x; + } +} +"; + var comp1 = CreateCompilation([source1, source2], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + CompileAndVerify(comp1, symbolValidator: validate).VerifyDiagnostics(); + + static void validate(ModuleSymbol m) + { + AssertEx.SequenceEqual( + ["Preserve1Attribute"], + m.GlobalNamespace.GetMember("Test1.g__local|0_0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } + + [Fact] + public void CompilerLoweringPreserveAttribute_03() + { + 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 = @" +class Test1 +{ + void M2<[Preserve1][Preserve2]T>(T x) + { + local(); + void local() => x.ToString(); + } +} +"; + var comp1 = CreateCompilation([source1, source2, CompilerLoweringPreserveAttributeDefinition], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + CompileAndVerify(comp1, symbolValidator: validate).VerifyDiagnostics(); + + static void validate(ModuleSymbol m) + { + AssertEx.SequenceEqual( + ["Preserve1Attribute"], + m.GlobalNamespace.GetMember("Test1.<>c__DisplayClass0_0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + AssertEx.SequenceEqual( + ["Preserve1Attribute", "Preserve2Attribute"], + m.GlobalNamespace.GetMember("Test1.g__local|0_0").TypeParameters.Single().GetAttributes().Select(a => a.ToString())); + } + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs index 4cef4ac1db904..7132b52637a15 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Retargeting/RetargetingTests.cs @@ -666,7 +666,7 @@ public void RetargetingUnmanagedTypeParameters(string code, bool isUnmanaged) var compilation = CreateCompilation(code).VerifyDiagnostics(); var sourceAssembly = (SourceAssemblySymbol)compilation.Assembly; - SourceTypeParameterSymbol sourceTypeParameter = (SourceTypeParameterSymbol)sourceAssembly.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); + SourceTypeTypeParameterSymbol sourceTypeParameter = (SourceTypeTypeParameterSymbol)sourceAssembly.GlobalNamespace.GetTypeMember("Test").TypeParameters.Single(); Assert.Equal(isUnmanaged, sourceTypeParameter.HasUnmanagedTypeConstraint); var retargetingAssembly = new RetargetingAssemblySymbol(sourceAssembly, isLinked: false);