From 987c5bfff0f554f23315e53ad42be3787e3de177 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 8 May 2023 11:22:16 -0700 Subject: [PATCH] Use a singleton for empty type-parameters for named types --- .../Symbols/Source/SourceNamedTypeSymbol.cs | 68 +++++++++++++++---- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 01950c5021b6d..54ae640a08c20 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; @@ -19,23 +20,47 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - // This is a type symbol associated with a type definition in source code. - // That is, for a generic type C this is the instance type C. - internal sealed partial class SourceNamedTypeSymbol : SourceMemberContainerTypeSymbol, IAttributeTargetSymbol + internal sealed class SourceNamedTypeSymbolTypeParameterInfo { - private ImmutableArray _lazyTypeParameters; + public readonly ImmutableArray TypeParameters; /// /// A collection of type parameter constraint types, populated when /// constraint types for the first type parameter are requested. /// - private ImmutableArray> _lazyTypeParameterConstraintTypes; + public ImmutableArray> LazyTypeParameterConstraintTypes; /// /// A collection of type parameter constraint kinds, populated when /// constraint kinds for the first type parameter are requested. /// - private ImmutableArray _lazyTypeParameterConstraintKinds; + public ImmutableArray LazyTypeParameterConstraintKinds; + + public static readonly SourceNamedTypeSymbolTypeParameterInfo Empty = new SourceNamedTypeSymbolTypeParameterInfo( + ImmutableArray.Empty, ImmutableArray>.Empty, ImmutableArray.Empty); + + private SourceNamedTypeSymbolTypeParameterInfo( + ImmutableArray typeParameters, + ImmutableArray> typeParameterConstraintTypes, + ImmutableArray typeParameterConstraintKinds) + { + TypeParameters = typeParameters; + LazyTypeParameterConstraintTypes = typeParameterConstraintTypes; + LazyTypeParameterConstraintKinds = typeParameterConstraintKinds; + } + + public static SourceNamedTypeSymbolTypeParameterInfo Create(ImmutableArray typeParameters) + { + // If we have no type parameters (common case), we can just point at the singleton empty instance. + return typeParameters.IsEmpty ? Empty : new SourceNamedTypeSymbolTypeParameterInfo(typeParameters, default, default); + } + } + + // This is a type symbol associated with a type definition in source code. + // That is, for a generic type C this is the instance type C. + internal sealed partial class SourceNamedTypeSymbol : SourceMemberContainerTypeSymbol, IAttributeTargetSymbol + { + private SourceNamedTypeSymbolTypeParameterInfo _lazyTypeParameterInfo; private CustomAttributesBag _lazyCustomAttributesBag; @@ -277,18 +302,24 @@ internal ImmutableArray GetTypeParameterConstraintTypes(int private ImmutableArray> GetTypeParameterConstraintTypes() { - var constraintTypes = _lazyTypeParameterConstraintTypes; + var constraintTypes = _lazyTypeParameterInfo?.LazyTypeParameterConstraintTypes ?? default; if (constraintTypes.IsDefault) { GetTypeParameterConstraintKinds(); var diagnostics = BindingDiagnosticBag.GetInstance(); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraintTypes, MakeTypeParameterConstraintTypes(diagnostics))) + var localConstraintTypes = MakeTypeParameterConstraintTypes(diagnostics); + Debug.Assert(_lazyTypeParameterInfo != null); + + if (ImmutableInterlocked.InterlockedInitialize( + ref _lazyTypeParameterInfo.LazyTypeParameterConstraintTypes, + localConstraintTypes)) { this.AddDeclarationDiagnostics(diagnostics); } + diagnostics.Free(); - constraintTypes = _lazyTypeParameterConstraintTypes; + constraintTypes = _lazyTypeParameterInfo.LazyTypeParameterConstraintTypes; } return constraintTypes; @@ -305,11 +336,15 @@ internal TypeParameterConstraintKind GetTypeParameterConstraintKind(int ordinal) private ImmutableArray GetTypeParameterConstraintKinds() { - var constraintKinds = _lazyTypeParameterConstraintKinds; + var constraintKinds = _lazyTypeParameterInfo?.LazyTypeParameterConstraintKinds ?? default; if (constraintKinds.IsDefault) { - ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraintKinds, MakeTypeParameterConstraintKinds()); - constraintKinds = _lazyTypeParameterConstraintKinds; + var localConstraintKinds = MakeTypeParameterConstraintKinds(); + Debug.Assert(_lazyTypeParameterInfo != null); + + ImmutableInterlocked.InterlockedInitialize( + ref _lazyTypeParameterInfo.LazyTypeParameterConstraintKinds, localConstraintKinds); + constraintKinds = _lazyTypeParameterInfo.LazyTypeParameterConstraintKinds; } return constraintKinds; @@ -749,10 +784,13 @@ public override ImmutableArray TypeParameters { get { - if (_lazyTypeParameters.IsDefault) + if (_lazyTypeParameterInfo is null) { var diagnostics = BindingDiagnosticBag.GetInstance(); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters, MakeTypeParameters(diagnostics))) + if (Interlocked.CompareExchange( + ref _lazyTypeParameterInfo, + SourceNamedTypeSymbolTypeParameterInfo.Create(MakeTypeParameters(diagnostics)), + comparand: null) is null) { AddDeclarationDiagnostics(diagnostics); } @@ -760,7 +798,7 @@ public override ImmutableArray TypeParameters diagnostics.Free(); } - return _lazyTypeParameters; + return _lazyTypeParameterInfo.TypeParameters; } }