Skip to content

Commit

Permalink
Add support for CompilerFeatureRequiredAttribute (#61113)
Browse files Browse the repository at this point in the history
Adds support for decoding and reporting errors when `CompilerFeatureRequiredAttribute` is encountered on metadata type symbols. We also block applying the attribute by hand in both C# and VB.
  • Loading branch information
333fred authored May 18, 2022
1 parent 9b63c76 commit 9d1754a
Show file tree
Hide file tree
Showing 88 changed files with 3,086 additions and 203 deletions.
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -7133,4 +7133,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="IDS_FeatureRelaxedShiftOperator" xml:space="preserve">
<value>relaxed shift operator</value>
</data>
<data name="ERR_UnsupportedCompilerFeature" xml:space="preserve">
<value>'{0}' requires compiler feature '{1}', which is not supported by this version of the C# compiler.</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2087,5 +2087,6 @@ internal enum ErrorCode
ERR_RequiredMembersBaseTypeInvalid = 9509,
ERR_ChainingToSetsRequiredMembersRequiresSetsRequiredMembers = 9510,
ERR_NewConstraintCannotHaveRequiredMembers = 9511,
ERR_UnsupportedCompilerFeature = 9512,
}
}
10 changes: 2 additions & 8 deletions src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,20 +307,14 @@ internal bool CalculateUseSiteDiagnostic(ref UseSiteInfo<AssemblySymbol> result)
return false;
}

protected override int HighestPriorityUseSiteError
{
get
{
return (int)ErrorCode.ERR_BindToBogus;
}
}
protected sealed override bool IsHighestPriorityUseSiteErrorCode(int code) => code is (int)ErrorCode.ERR_UnsupportedCompilerFeature or (int)ErrorCode.ERR_BindToBogus;

public sealed override bool HasUnsupportedMetadata
{
get
{
DiagnosticInfo? info = GetUseSiteInfo().DiagnosticInfo;
return (object?)info != null && info.Code == (int)ErrorCode.ERR_BindToBogus;
return (object?)info != null && info.Code is (int)ErrorCode.ERR_BindToBogus or (int)ErrorCode.ERR_UnsupportedCompilerFeature;
}
}

Expand Down
12 changes: 3 additions & 9 deletions src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -373,22 +373,16 @@ internal bool CalculateUseSiteDiagnostic(ref UseSiteInfo<AssemblySymbol> result)
}

/// <summary>
/// Return error code that has highest priority while calculating use site error for this symbol.
/// Returns true if the error code is highest priority while calculating use site error for this symbol.
/// </summary>
protected override int HighestPriorityUseSiteError
{
get
{
return (int)ErrorCode.ERR_BindToBogus;
}
}
protected sealed override bool IsHighestPriorityUseSiteErrorCode(int code) => code is (int)ErrorCode.ERR_UnsupportedCompilerFeature or (int)ErrorCode.ERR_BindToBogus;

public sealed override bool HasUnsupportedMetadata
{
get
{
DiagnosticInfo info = GetUseSiteInfo().DiagnosticInfo;
return (object)info != null && info.Code == (int)ErrorCode.ERR_BindToBogus;
return (object)info != null && info.Code is (int)ErrorCode.ERR_BindToBogus or (int)ErrorCode.ERR_UnsupportedCompilerFeature;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
Expand Down Expand Up @@ -63,6 +64,10 @@ internal sealed class PEAssemblySymbol : MetadataOrSourceAssemblySymbol
/// </summary>
private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;

#nullable enable
private DiagnosticInfo? _lazyCachedCompilerFeatureRequiredDiagnosticInfo = CSDiagnosticInfo.EmptyErrorInfo;
#nullable disable

internal PEAssemblySymbol(PEAssembly assembly, DocumentationProvider documentationProvider, bool isLinked, MetadataImportOptions importOptions)
{
Debug.Assert(assembly != null);
Expand Down Expand Up @@ -280,5 +285,22 @@ internal PEModuleSymbol PrimaryModule
}

public override AssemblyMetadata GetMetadata() => _assembly.GetNonDisposableMetadata();

#nullable enable
internal DiagnosticInfo? GetCompilerFeatureRequiredDiagnostic()
{
if (_lazyCachedCompilerFeatureRequiredDiagnosticInfo == CSDiagnosticInfo.EmptyErrorInfo)
{
Interlocked.CompareExchange(
ref _lazyCachedCompilerFeatureRequiredDiagnosticInfo,
PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, PrimaryModule, this.Assembly.Handle, CompilerFeatureRequiredFeatures.None, new MetadataDecoder(PrimaryModule)),
CSDiagnosticInfo.EmptyErrorInfo);
}

return _lazyCachedCompilerFeatureRequiredDiagnosticInfo;
}

public override bool HasUnsupportedMetadata
=> GetCompilerFeatureRequiredDiagnostic()?.Code == (int)ErrorCode.ERR_UnsupportedCompilerFeature || base.HasUnsupportedMetadata;
}
}
20 changes: 20 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEEventSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -468,10 +468,30 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
deriveCompilerFeatureRequiredUseSiteInfo(ref result);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency);

void deriveCompilerFeatureRequiredUseSiteInfo(ref UseSiteInfo<AssemblySymbol> result)
{
var containingType = (PENamedTypeSymbol)ContainingType;
PEModuleSymbol containingPEModule = _containingType.ContainingPEModule;
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(
this,
containingPEModule,
Handle,
allowedFeatures: CompilerFeatureRequiredFeatures.None,
new MetadataDecoder(containingPEModule, containingType));

diag ??= containingType.GetCompilerFeatureRequiredDiagnostic();

if (diag != null)
{
result = new UseSiteInfo<AssemblySymbol>(diag);
}
}
}

internal override ObsoleteAttributeData ObsoleteAttributeData
Expand Down
20 changes: 20 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEFieldSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -590,10 +590,30 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
UseSiteInfo<AssemblySymbol> result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
deriveCompilerFeatureRequiredUseSiteInfo(ref result);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);
}

return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency);

void deriveCompilerFeatureRequiredUseSiteInfo(ref UseSiteInfo<AssemblySymbol> result)
{
var containingType = (PENamedTypeSymbol)ContainingType;
PEModuleSymbol containingPEModule = _containingType.ContainingPEModule;
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(
this,
containingPEModule,
Handle,
allowedFeatures: CompilerFeatureRequiredFeatures.None,
new MetadataDecoder(containingPEModule, containingType));

diag ??= containingType.GetCompilerFeatureRequiredDiagnostic();

if (diag != null)
{
result = new UseSiteInfo<AssemblySymbol>(diag);
}
}
}

internal override ObsoleteAttributeData ObsoleteAttributeData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,7 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
CalculateUseSiteDiagnostic(ref result);

var diagnosticInfo = result.DiagnosticInfo;
MergeUseSiteDiagnostics(ref diagnosticInfo, DeriveCompilerFeatureRequiredDiagnostic());
EnsureTypeParametersAreLoaded(ref diagnosticInfo);
if (diagnosticInfo == null && GetUnmanagedCallersOnlyAttributeData(forceComplete: true) is UnmanagedCallersOnlyAttributeData data)
{
Expand All @@ -1380,6 +1381,44 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
return GetCachedUseSiteInfo();
}

private DiagnosticInfo DeriveCompilerFeatureRequiredDiagnostic()
{
var containingModule = _containingType.ContainingPEModule;
var decoder = new MetadataDecoder(containingModule, this);
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, containingModule, Handle, allowedFeatures: MethodKind == MethodKind.Constructor ? CompilerFeatureRequiredFeatures.RequiredMembers : CompilerFeatureRequiredFeatures.None, decoder);

if (diag != null)
{
return diag;
}

diag = Signature.ReturnParam.DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}

foreach (var param in Parameters)
{
diag = ((PEParameterSymbol)param).DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}
}

foreach (var typeParam in TypeParameters)
{
diag = ((PETypeParameterSymbol)typeParam).DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}
}

return _containingType.GetCompilerFeatureRequiredDiagnostic();
}

private UseSiteInfo<AssemblySymbol> GetCachedUseSiteInfo()
{
return (_uncommonFields?._lazyCachedUseSiteInfo ?? default).ToUseSiteInfo(PrimaryDependency);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ private enum NullableMemberMetadata

private NullableMemberMetadata _lazyNullableMemberMetadata;

#nullable enable
private DiagnosticInfo? _lazyCachedCompilerFeatureRequiredDiagnosticInfo = CSDiagnosticInfo.EmptyErrorInfo;
#nullable disable

internal PEModuleSymbol(PEAssemblySymbol assemblySymbol, PEModule module, MetadataImportOptions importOptions, int ordinal)
: this((AssemblySymbol)assemblySymbol, module, importOptions, ordinal)
{
Expand Down Expand Up @@ -755,5 +759,22 @@ internal bool ShouldDecodeNullableAttributes(Symbol symbol)

return false;
}

#nullable enable
internal DiagnosticInfo? GetCompilerFeatureRequiredDiagnostic()
{
if (_lazyCachedCompilerFeatureRequiredDiagnosticInfo == CSDiagnosticInfo.EmptyErrorInfo)
{
Interlocked.CompareExchange(
ref _lazyCachedCompilerFeatureRequiredDiagnosticInfo,
PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, this, Token, CompilerFeatureRequiredFeatures.None, new MetadataDecoder(this)),
CSDiagnosticInfo.EmptyErrorInfo);
}

return _lazyCachedCompilerFeatureRequiredDiagnosticInfo ?? (_assemblySymbol as PEAssemblySymbol)?.GetCompilerFeatureRequiredDiagnostic();
}

public override bool HasUnsupportedMetadata
=> GetCompilerFeatureRequiredDiagnostic()?.Code == (int)ErrorCode.ERR_UnsupportedCompilerFeature || base.HasUnsupportedMetadata;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ internal static PENamedTypeSymbol Create(

if (mrEx != null)
{
result._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BogusType, result));
result._lazyCachedUseSiteInfo.Initialize(result.DeriveCompilerFeatureRequiredDiagnostic() ?? new CSDiagnosticInfo(ErrorCode.ERR_BogusType, result));
}

return result;
Expand Down Expand Up @@ -261,7 +261,7 @@ internal static PENamedTypeSymbol Create(

if (mrEx != null || metadataArity < containerMetadataArity)
{
result._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BogusType, result));
result._lazyCachedUseSiteInfo.Initialize(result.DeriveCompilerFeatureRequiredDiagnostic() ?? new CSDiagnosticInfo(ErrorCode.ERR_BogusType, result));
}

return result;
Expand Down Expand Up @@ -331,7 +331,7 @@ private PENamedTypeSymbol(

if (makeBad)
{
_lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this));
_lazyCachedUseSiteInfo.Initialize(DeriveCompilerFeatureRequiredDiagnostic() ?? new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this));
}
}

Expand Down Expand Up @@ -2024,7 +2024,14 @@ internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()

protected virtual DiagnosticInfo GetUseSiteDiagnosticImpl()
{
DiagnosticInfo diagnostic = null;
// GetCompilerFeatureRequiredDiagnostic depends on UnsupportedCompilerFeature being the highest priority diagnostic, or it will return incorrect
// results and assert in Debug mode.
DiagnosticInfo diagnostic = DeriveCompilerFeatureRequiredDiagnostic();

if (diagnostic != null)
{
return diagnostic;
}

if (!MergeUseSiteDiagnostics(ref diagnostic, CalculateUseSiteDiagnostic()))
{
Expand Down Expand Up @@ -2060,6 +2067,45 @@ protected virtual DiagnosticInfo GetUseSiteDiagnosticImpl()
return diagnostic;
}

#nullable enable
internal DiagnosticInfo? GetCompilerFeatureRequiredDiagnostic()
{
var useSiteInfo = GetUseSiteInfo();
if (useSiteInfo.DiagnosticInfo is { Code: (int)ErrorCode.ERR_UnsupportedCompilerFeature } diag)
{
return diag;
}

Debug.Assert(DeriveCompilerFeatureRequiredDiagnostic() is null);
return null;
}

private DiagnosticInfo? DeriveCompilerFeatureRequiredDiagnostic()
{
var decoder = new MetadataDecoder(ContainingPEModule, this);
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, ContainingPEModule, Handle, allowedFeatures: IsRefLikeType ? CompilerFeatureRequiredFeatures.RefStructs : CompilerFeatureRequiredFeatures.None, decoder);

if (diag != null)
{
return diag;
}

foreach (var typeParameter in TypeParameters)
{
diag = ((PETypeParameterSymbol)typeParameter).DeriveCompilerFeatureRequiredDiagnostic(decoder);

if (diag != null)
{
return diag;
}
}

return ContainingType is PENamedTypeSymbol containingType
? containingType.GetCompilerFeatureRequiredDiagnostic()
: ContainingPEModule.GetCompilerFeatureRequiredDiagnostic();
}
#nullable disable

internal string DefaultMemberName
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1068,5 +1068,25 @@ public sealed override bool Equals(Symbol other, TypeCompareKind compareKind)
nps.Equals(this, compareKind) :
base.Equals(other, compareKind);
}

#nullable enable
internal DiagnosticInfo? DeriveCompilerFeatureRequiredDiagnostic(MetadataDecoder decoder)
=> PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, (PEModuleSymbol)ContainingModule, Handle, allowedFeatures: CompilerFeatureRequiredFeatures.None, decoder);

public override bool HasUnsupportedMetadata
{
get
{
var containingModule = (PEModuleSymbol)ContainingModule;
var decoder = ContainingSymbol switch
{
PEMethodSymbol method => new MetadataDecoder(containingModule, method),
PEPropertySymbol => new MetadataDecoder(containingModule, (PENamedTypeSymbol)ContainingType),
_ => throw ExceptionUtilities.UnexpectedValue(this.ContainingSymbol.Kind)
};

return DeriveCompilerFeatureRequiredDiagnostic(decoder) is { Code: (int)ErrorCode.ERR_UnsupportedCompilerFeature } || base.HasUnsupportedMetadata;
}
}
}
}
Loading

0 comments on commit 9d1754a

Please sign in to comment.