Skip to content

Commit

Permalink
Move PEPropertySymbol to use the same UncommonFields pattern that PEM…
Browse files Browse the repository at this point in the history
…ethodSymbol uses (#74132)

This will be useful for OverloadResolutionPriority as well, as most properties won't have this attribute.
  • Loading branch information
333fred authored Jun 27, 2024
1 parent 5d8b9b8 commit 5475105
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1485,7 +1485,7 @@ private UseSiteInfo<AssemblySymbol> InitializeUseSiteDiagnostic(UseSiteInfo<Asse

if (useSiteInfo.DiagnosticInfo is object || !useSiteInfo.SecondaryDependencies.IsNullOrEmpty())
{
useSiteInfo = AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitialize(PrimaryDependency, useSiteInfo);
useSiteInfo = AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitializeFromDefault(PrimaryDependency, useSiteInfo);
}

_packedFlags.SetIsUseSiteDiagnosticPopulated();
Expand Down
170 changes: 148 additions & 22 deletions src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEPropertySymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@ internal class PEPropertySymbol
private readonly TypeWithAnnotations _propertyTypeWithAnnotations;
private readonly PEMethodSymbol _getMethod;
private readonly PEMethodSymbol _setMethod;
private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
private Tuple<CultureInfo, string> _lazyDocComment;
private CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;

private ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
#nullable enable
private UncommonFields? _uncommonFields;
#nullable disable

// CONSIDER: the parameters could be computed lazily (as in PEMethodSymbol).
// CONSIDER: if the parameters were computed lazily, ParameterCount could be overridden to fall back on the signature (as in PEMethodSymbol).
Expand All @@ -53,20 +51,26 @@ internal class PEPropertySymbol
private struct PackedFlags
{
// Layout:
// |.........................|uu|rr|c|n|s|
// |....................c|o|d|uu|rr|c|n|s|
//
// s = special name flag. 1 bit
// n = runtime special name flag. 1 bit
// c = call methods directly flag. 1 bit
// r = Required member. 2 bits (1 bit for value + 1 completion bit).
// u = Unscoped ref. 2 bits (1 bit for value + 1 completion bit).
// d = Use site diagnostic flag. 1 bit
// o = Obsolete flag. 1 bit
// c = Custom attributes flag. 1 bit
private const int IsSpecialNameFlag = 1 << 0;
private const int IsRuntimeSpecialNameFlag = 1 << 1;
private const int CallMethodsDirectlyFlag = 1 << 2;
private const int HasRequiredMemberAttribute = 1 << 4;
private const int RequiredMemberCompletionBit = 1 << 5;
private const int HasUnscopedRefAttribute = 1 << 6;
private const int UnscopedRefCompletionBit = 1 << 7;
private const int IsUseSiteDiagnosticPopulatedBit = 1 << 8;
private const int IsObsoleteAttributePopulatedBit = 1 << 9;
private const int IsCustomAttributesPopulatedBit = 1 << 10;

private int _bits;

Expand All @@ -83,7 +87,7 @@ public void SetHasRequiredMemberAttribute(bool isRequired)
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}

public bool TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute)
public readonly bool TryGetHasRequiredMemberAttribute(out bool hasRequiredMemberAttribute)
{
if ((_bits & RequiredMemberCompletionBit) != 0)
{
Expand All @@ -101,7 +105,7 @@ public void SetHasUnscopedRefAttribute(bool unscopedRef)
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}

public bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute)
public readonly bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute)
{
if ((_bits & UnscopedRefCompletionBit) != 0)
{
Expand All @@ -113,9 +117,38 @@ public bool TryGetHasUnscopedRefAttribute(out bool hasUnscopedRefAttribute)
return false;
}

public bool IsSpecialName => (_bits & IsSpecialNameFlag) != 0;
public bool IsRuntimeSpecialName => (_bits & IsRuntimeSpecialNameFlag) != 0;
public bool CallMethodsDirectly => (_bits & CallMethodsDirectlyFlag) != 0;
public readonly bool IsSpecialName => (_bits & IsSpecialNameFlag) != 0;
public readonly bool IsRuntimeSpecialName => (_bits & IsRuntimeSpecialNameFlag) != 0;
public readonly bool CallMethodsDirectly => (_bits & CallMethodsDirectlyFlag) != 0;

public void SetUseSiteDiagnosticPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsUseSiteDiagnosticPopulatedBit);
}

public readonly bool IsUseSiteDiagnosticPopulated => (_bits & IsUseSiteDiagnosticPopulatedBit) != 0;

public void SetObsoleteAttributePopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsObsoleteAttributePopulatedBit);
}

public readonly bool IsObsoleteAttributePopulated => (_bits & IsObsoleteAttributePopulatedBit) != 0;

public void SetCustomAttributesPopulated()
{
ThreadSafeFlagOperations.Set(ref _bits, IsCustomAttributesPopulatedBit);
}

public readonly bool IsCustomAttributesPopulated => (_bits & IsCustomAttributesPopulatedBit) != 0;
}

private sealed class UncommonFields
{
public ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
public Tuple<CultureInfo, string> _lazyDocComment;
public CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;
public ObsoleteAttributeData _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
}

internal static PEPropertySymbol Create(
Expand Down Expand Up @@ -146,7 +179,8 @@ internal static PEPropertySymbol Create(

if (propEx != null || isBad)
{
result._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, result));
result.AccessUncommonFields()._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, result));
result._flags.SetUseSiteDiagnosticPopulated();
}

return result;
Expand Down Expand Up @@ -201,7 +235,8 @@ private PEPropertySymbol(

if (getEx != null || setEx != null || mrEx != null || isBad)
{
_lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this));
AccessUncommonFields()._lazyCachedUseSiteInfo.Initialize(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this));
_flags.SetUseSiteDiagnosticPopulated();
}

var returnInfo = propertyParams[0];
Expand Down Expand Up @@ -277,6 +312,33 @@ static bool anyUnexpectedRequiredModifiers(ParamInfo<TypeSymbol>[] propertyParam
}
}

private UncommonFields CreateUncommonFields()
{
var retVal = new UncommonFields();
if (!_flags.IsObsoleteAttributePopulated)
{
retVal._lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
}

if (!_flags.IsUseSiteDiagnosticPopulated)
{
retVal._lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;
}

if (_flags.IsCustomAttributesPopulated)
{
retVal._lazyCustomAttributes = ImmutableArray<CSharpAttributeData>.Empty;
}

return retVal;
}

private UncommonFields AccessUncommonFields()
{
var retVal = _uncommonFields;
return retVal ?? InterlockedOperations.Initialize(ref _uncommonFields, CreateUncommonFields());
}

private bool MustCallMethodsDirectlyCore()
{
if (this.RefKind != RefKind.None && _setMethod != null)
Expand Down Expand Up @@ -635,7 +697,7 @@ public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences

public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
if (_lazyCustomAttributes.IsDefault)
if (!_flags.IsCustomAttributesPopulated)
{
var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule;

Expand All @@ -646,10 +708,31 @@ public override ImmutableArray<CSharpAttributeData> GetAttributes()
out CustomAttributeHandle required,
AttributeDescription.RequiredMemberAttribute);

ImmutableInterlocked.InterlockedInitialize(ref _lazyCustomAttributes, attributes);
if (!attributes.IsEmpty)
{
ImmutableInterlocked.InterlockedInitialize(ref AccessUncommonFields()._lazyCustomAttributes, attributes);
}

_flags.SetCustomAttributesPopulated();
_flags.SetHasRequiredMemberAttribute(!required.IsNil);
}
return _lazyCustomAttributes;

var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return ImmutableArray<CSharpAttributeData>.Empty;
}
else
{
var result = uncommonFields._lazyCustomAttributes;
if (result.IsDefault)
{
result = ImmutableArray<CSharpAttributeData>.Empty;
ImmutableInterlocked.InterlockedInitialize(ref uncommonFields._lazyCustomAttributes, result);
}

return result;
}
}

internal override IEnumerable<CSharpAttributeData> GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder)
Expand Down Expand Up @@ -814,24 +897,45 @@ private static ImmutableArray<ParameterSymbol> GetParameters(

public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
return PEDocumentationCommentUtils.GetDocumentationComment(this, _containingType.ContainingPEModule, preferredCulture, cancellationToken, ref _lazyDocComment);
return PEDocumentationCommentUtils.GetDocumentationComment(this, _containingType.ContainingPEModule, preferredCulture, cancellationToken, ref AccessUncommonFields()._lazyDocComment);
}

internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
AssemblySymbol primaryDependency = PrimaryDependency;

if (!_lazyCachedUseSiteInfo.IsInitialized)
if (!_flags.IsUseSiteDiagnosticPopulated)
{
var result = new UseSiteInfo<AssemblySymbol>(primaryDependency);
CalculateUseSiteDiagnostic(ref result);
var diag = deriveCompilerFeatureRequiredUseSiteInfo();
MergeUseSiteDiagnostics(ref diag, result.DiagnosticInfo);
result = result.AdjustDiagnosticInfo(diag);
_lazyCachedUseSiteInfo.Initialize(primaryDependency, result);

if (result.DiagnosticInfo is not null || !result.SecondaryDependencies.IsNullOrEmpty())
{
AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitializeFromSentinel(PrimaryDependency, result);
}

_flags.SetUseSiteDiagnosticPopulated();
}

return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency);
var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return new UseSiteInfo<AssemblySymbol>(primaryDependency);
}
else
{
var result = uncommonFields._lazyCachedUseSiteInfo;
if (!result.IsInitialized)
{
uncommonFields._lazyCachedUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, new UseSiteInfo<AssemblySymbol>(primaryDependency));
result = uncommonFields._lazyCachedUseSiteInfo;
}

return result.ToUseSiteInfo(primaryDependency);
}

DiagnosticInfo deriveCompilerFeatureRequiredUseSiteInfo()
{
Expand Down Expand Up @@ -867,8 +971,30 @@ internal override ObsoleteAttributeData ObsoleteAttributeData
{
get
{
ObsoleteAttributeHelpers.InitializeObsoleteDataFromMetadata(ref _lazyObsoleteAttributeData, _handle, (PEModuleSymbol)(this.ContainingModule), ignoreByRefLikeMarker: false, ignoreRequiredMemberMarker: false);
return _lazyObsoleteAttributeData;
if (!_flags.IsObsoleteAttributePopulated)
{
var result = ObsoleteAttributeHelpers.GetObsoleteDataFromMetadata(_handle, (PEModuleSymbol)(this.ContainingModule), ignoreByRefLikeMarker: false, ignoreRequiredMemberMarker: false);
if (result != null)
{
result = InterlockedOperations.Initialize(ref AccessUncommonFields()._lazyObsoleteAttributeData, result, ObsoleteAttributeData.Uninitialized);
}

_flags.SetObsoleteAttributePopulated();
return result;
}

var uncommonFields = _uncommonFields;
if (uncommonFields == null)
{
return null;
}
else
{
var result = uncommonFields._lazyObsoleteAttributeData;
return ReferenceEquals(result, ObsoleteAttributeData.Uninitialized)
? InterlockedOperations.Initialize(ref uncommonFields._lazyObsoleteAttributeData, initializedValue: null, ObsoleteAttributeData.Uninitialized)
: result;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ private ImmutableArray<TypeWithAnnotations> GetDeclaredConstraintTypes(ConsList<
{
// we do not recognize these combinations as "unmanaged"
hasUnmanagedModreqPattern = false;
_lazyCachedConstraintsUseSiteInfo.InterlockedCompareExchange(primaryDependency: null, new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
_lazyCachedConstraintsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency: null, new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
}

_lazyHasIsUnmanagedConstraint = hasUnmanagedModreqPattern.ToThreeState();
Expand Down Expand Up @@ -416,7 +416,7 @@ private GenericParameterConstraintHandleCollection GetConstraintHandleCollection
catch (BadImageFormatException)
{
constraints = default(GenericParameterConstraintHandleCollection);
_lazyCachedConstraintsUseSiteInfo.InterlockedCompareExchange(primaryDependency: null, new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
_lazyCachedConstraintsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency: null, new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this)));
}

return constraints;
Expand Down Expand Up @@ -689,7 +689,7 @@ private TypeParameterBounds GetBounds(ConsList<TypeParameterSymbol> inProgress)

diagnostics.Free();

_lazyCachedConstraintsUseSiteInfo.InterlockedCompareExchange(primaryDependency, useSiteInfo);
_lazyCachedConstraintsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, useSiteInfo);
Interlocked.CompareExchange(ref _lazyBounds, bounds, TypeParameterBounds.Unset);
}

Expand Down
8 changes: 6 additions & 2 deletions src/Compilers/Core/Portable/Binding/UseSiteInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ private void Initialize(DiagnosticInfo? diagnosticInfo, ImmutableHashSet<TAssemb
return info;
}

public void InterlockedCompareExchange(TAssemblySymbol? primaryDependency, UseSiteInfo<TAssemblySymbol> value)
public void InterlockedInitializeFromSentinel(TAssemblySymbol? primaryDependency, UseSiteInfo<TAssemblySymbol> value)
{
if ((object?)_info == Sentinel)
{
Expand All @@ -526,7 +526,11 @@ public void InterlockedCompareExchange(TAssemblySymbol? primaryDependency, UseSi
}
}

public UseSiteInfo<TAssemblySymbol> InterlockedInitialize(TAssemblySymbol? primaryDependency, UseSiteInfo<TAssemblySymbol> value)
/// <summary>
/// Atomically initializes the cache with the given value if it is currently fully default.
/// This <i>will not</i> initialize <see cref="CachedUseSiteInfo{TAssemblySymbol}.Uninitialized"/>.
/// </summary>
public UseSiteInfo<TAssemblySymbol> InterlockedInitializeFromDefault(TAssemblySymbol? primaryDependency, UseSiteInfo<TAssemblySymbol> value)
{
object? info = Compact(value.DiagnosticInfo, GetDependenciesToCache(primaryDependency, value));
Debug.Assert(info is object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
End If

If useSiteInfo.DiagnosticInfo IsNot Nothing OrElse Not useSiteInfo.SecondaryDependencies.IsNullOrEmpty() Then
useSiteInfo = AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitialize(PrimaryDependency, useSiteInfo)
useSiteInfo = AccessUncommonFields()._lazyCachedUseSiteInfo.InterlockedInitializeFromDefault(PrimaryDependency, useSiteInfo)
End If

_packedFlags.SetIsUseSiteDiagnosticPopulated()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
constraints = metadataReader.GetGenericParameter(_handle).GetConstraints()
Catch mrEx As BadImageFormatException
constraints = Nothing
_lazyCachedBoundsUseSiteInfo.InterlockedCompareExchange(primaryDependency:=Nothing, New UseSiteInfo(Of AssemblySymbol)(ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedType1, Me)))
_lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency:=Nothing, New UseSiteInfo(Of AssemblySymbol)(ErrorFactory.ErrorInfo(ERRID.ERR_UnsupportedType1, Me)))
End Try

If constraints.Count > 0 Then
Expand Down Expand Up @@ -298,7 +298,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE

diagnosticsBuilder.Free()

_lazyCachedBoundsUseSiteInfo.InterlockedCompareExchange(primaryDependency, useSiteInfo)
_lazyCachedBoundsUseSiteInfo.InterlockedInitializeFromSentinel(primaryDependency, useSiteInfo)
ImmutableInterlocked.InterlockedInitialize(_lazyConstraintTypes, GetConstraintTypesOnly(constraints))
End If

Expand Down

0 comments on commit 5475105

Please sign in to comment.