diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 01950c5021b6d..e3029c48fcb94 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -23,19 +23,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols // That is, for a generic type C this is the instance type C. internal sealed partial class SourceNamedTypeSymbol : SourceMemberContainerTypeSymbol, IAttributeTargetSymbol { - private ImmutableArray _lazyTypeParameters; - - /// - /// A collection of type parameter constraint types, populated when - /// constraint types for the first type parameter are requested. - /// - private ImmutableArray> _lazyTypeParameterConstraintTypes; - - /// - /// A collection of type parameter constraint kinds, populated when - /// constraint kinds for the first type parameter are requested. - /// - private ImmutableArray _lazyTypeParameterConstraintKinds; + private readonly TypeParameterInfo _typeParameterInfo; private CustomAttributesBag _lazyCustomAttributesBag; @@ -104,6 +92,10 @@ internal SourceNamedTypeSymbol(NamespaceOrTypeSymbol containingSymbol, MergedTyp // Nested types are never unified. _lazyIsExplicitDefinitionOfNoPiaLocalType = ThreeState.False; } + + _typeParameterInfo = declaration.Arity == 0 + ? TypeParameterInfo.Empty + : new TypeParameterInfo(); } protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) @@ -277,21 +269,22 @@ internal ImmutableArray GetTypeParameterConstraintTypes(int private ImmutableArray> GetTypeParameterConstraintTypes() { - var constraintTypes = _lazyTypeParameterConstraintTypes; - if (constraintTypes.IsDefault) + if (_typeParameterInfo.LazyTypeParameterConstraintTypes.IsDefault) { GetTypeParameterConstraintKinds(); var diagnostics = BindingDiagnosticBag.GetInstance(); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraintTypes, MakeTypeParameterConstraintTypes(diagnostics))) + if (ImmutableInterlocked.InterlockedInitialize( + ref _typeParameterInfo.LazyTypeParameterConstraintTypes, + MakeTypeParameterConstraintTypes(diagnostics))) { this.AddDeclarationDiagnostics(diagnostics); } diagnostics.Free(); - constraintTypes = _lazyTypeParameterConstraintTypes; } - return constraintTypes; + Debug.Assert(!_typeParameterInfo.LazyTypeParameterConstraintTypes.IsDefault); + return _typeParameterInfo.LazyTypeParameterConstraintTypes; } /// @@ -305,14 +298,15 @@ internal TypeParameterConstraintKind GetTypeParameterConstraintKind(int ordinal) private ImmutableArray GetTypeParameterConstraintKinds() { - var constraintKinds = _lazyTypeParameterConstraintKinds; - if (constraintKinds.IsDefault) + if (_typeParameterInfo.LazyTypeParameterConstraintKinds.IsDefault) { - ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraintKinds, MakeTypeParameterConstraintKinds()); - constraintKinds = _lazyTypeParameterConstraintKinds; + ImmutableInterlocked.InterlockedInitialize( + ref _typeParameterInfo.LazyTypeParameterConstraintKinds, + MakeTypeParameterConstraintKinds()); } - return constraintKinds; + Debug.Assert(!_typeParameterInfo.LazyTypeParameterConstraintKinds.IsDefault); + return _typeParameterInfo.LazyTypeParameterConstraintKinds; } private ImmutableArray> MakeTypeParameterConstraintTypes(BindingDiagnosticBag diagnostics) @@ -749,10 +743,12 @@ public override ImmutableArray TypeParameters { get { - if (_lazyTypeParameters.IsDefault) + if (_typeParameterInfo.LazyTypeParameters.IsDefault) { var diagnostics = BindingDiagnosticBag.GetInstance(); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters, MakeTypeParameters(diagnostics))) + if (ImmutableInterlocked.InterlockedInitialize( + ref _typeParameterInfo.LazyTypeParameters, + MakeTypeParameters(diagnostics))) { AddDeclarationDiagnostics(diagnostics); } @@ -760,7 +756,7 @@ public override ImmutableArray TypeParameters diagnostics.Free(); } - return _lazyTypeParameters; + return _typeParameterInfo.LazyTypeParameters; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index 55127be6d829e..b619030b5f6ec 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; @@ -25,19 +24,7 @@ internal sealed class SourceOrdinaryMethodSymbol : SourceOrdinaryMethodSymbolBas private readonly RefKind _refKind; private bool _lazyIsVararg; - /// - /// A collection of type parameter constraint types, populated when - /// constraint types for the first type parameter is requested. - /// Initialized in two steps. Hold a copy if accessing during initialization. - /// - private ImmutableArray> _lazyTypeParameterConstraintTypes; - - /// - /// A collection of type parameter constraint kinds, populated when - /// constraint kinds for the first type parameter is requested. - /// Initialized in two steps. Hold a copy if accessing during initialization. - /// - private ImmutableArray _lazyTypeParameterConstraintKinds; + private readonly TypeParameterInfo _typeParameterInfo; /// /// If this symbol represents a partial method definition or implementation part, its other part (if any). @@ -92,6 +79,13 @@ private SourceOrdinaryMethodSymbol( { Debug.Assert(diagnostics.DiagnosticBag is object); + // Compute the type parameters. If empty (the common case), directly point at the singleton to reduce the + // amount of pointers-to-arrays this type needs to store. + var typeParameters = MakeTypeParameters(syntax, diagnostics); + _typeParameterInfo = typeParameters.IsEmpty + ? TypeParameterInfo.Empty + : new TypeParameterInfo { LazyTypeParameters = typeParameters }; + _explicitInterfaceType = explicitInterfaceType; bool hasBlockBody = syntax.Body != null; @@ -105,17 +99,12 @@ private SourceOrdinaryMethodSymbol( syntax.Body, syntax.ExpressionBody, syntax, diagnostics); } - protected override ImmutableArray MakeTypeParameters(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics) + public override ImmutableArray TypeParameters { - var syntax = (MethodDeclarationSyntax)node; - if (syntax.Arity == 0) - { - ReportErrorIfHasConstraints(syntax.ConstraintClauses, diagnostics.DiagnosticBag); - return ImmutableArray.Empty; - } - else + get { - return MakeTypeParameters(syntax, diagnostics); + Debug.Assert(!_typeParameterInfo.LazyTypeParameters.IsDefault); + return _typeParameterInfo.LazyTypeParameters; } } @@ -308,7 +297,7 @@ protected override void CompleteAsyncMethodChecksBetweenStartAndFinish() public override ImmutableArray> GetTypeParameterConstraintTypes() { - if (_lazyTypeParameterConstraintTypes.IsDefault) + if (_typeParameterInfo.LazyTypeParameterConstraintTypes.IsDefault) { GetTypeParameterConstraintKinds(); @@ -324,19 +313,21 @@ public override ImmutableArray> GetTypeParam syntax.TypeParameterList, syntax.ConstraintClauses, diagnostics); - if (ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraintTypes, constraints)) + if (ImmutableInterlocked.InterlockedInitialize( + ref _typeParameterInfo.LazyTypeParameterConstraintTypes, + constraints)) { this.AddDeclarationDiagnostics(diagnostics); } diagnostics.Free(); } - return _lazyTypeParameterConstraintTypes; + return _typeParameterInfo.LazyTypeParameterConstraintTypes; } public override ImmutableArray GetTypeParameterConstraintKinds() { - if (_lazyTypeParameterConstraintKinds.IsDefault) + if (_typeParameterInfo.LazyTypeParameterConstraintKinds.IsDefault) { var syntax = GetSyntax(); var withTypeParametersBinder = @@ -349,10 +340,12 @@ public override ImmutableArray GetTypeParameterCons syntax.TypeParameterList, syntax.ConstraintClauses); - ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameterConstraintKinds, constraints); + ImmutableInterlocked.InterlockedInitialize( + ref _typeParameterInfo.LazyTypeParameterConstraintKinds, + constraints); } - return _lazyTypeParameterConstraintKinds; + return _typeParameterInfo.LazyTypeParameterConstraintKinds; } public override bool IsVararg @@ -530,6 +523,12 @@ protected override DeclarationModifiers MakeDeclarationModifiers(DeclarationModi private ImmutableArray MakeTypeParameters(MethodDeclarationSyntax syntax, BindingDiagnosticBag diagnostics) { + if (syntax.Arity == 0) + { + ReportErrorIfHasConstraints(syntax.ConstraintClauses, diagnostics.DiagnosticBag); + return ImmutableArray.Empty; + } + Debug.Assert(syntax.TypeParameterList != null); MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, syntax.TypeParameterList.LessThanToken); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs index 9769624caabcf..2eaef04464ab7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -23,7 +23,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// internal abstract class SourceOrdinaryMethodSymbolBase : SourceOrdinaryMethodOrUserDefinedOperatorSymbol { - private readonly ImmutableArray _typeParameters; private readonly string _name; protected SourceOrdinaryMethodSymbolBase( @@ -62,8 +61,6 @@ protected SourceOrdinaryMethodSymbolBase( this.MakeFlags(methodKind, declarationModifiers, returnsVoid, isExtensionMethod: isExtensionMethod, isNullableAnalysisEnabled: isNullableAnalysisEnabled, isMetadataVirtualIgnoringModifiers: isMetadataVirtualIgnoringModifiers); - _typeParameters = MakeTypeParameters(syntax, diagnostics); - CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody, diagnostics); if (hasBody) @@ -74,8 +71,6 @@ protected SourceOrdinaryMethodSymbolBase( ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation: isExplicitInterfaceImplementation, diagnostics, location); } - protected abstract ImmutableArray MakeTypeParameters(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics); - #nullable enable protected override void MethodChecks(BindingDiagnosticBag diagnostics) { @@ -89,7 +84,7 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) { for (int i = 0; i < declaredConstraints.Length; i++) { - var typeParameter = _typeParameters[i]; + var typeParameter = this.TypeParameters[i]; ErrorCode report; switch (declaredConstraints[i].Constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.Default)) @@ -171,10 +166,7 @@ private void CompleteAsyncMethodChecks(BindingDiagnosticBag diagnosticsOpt, Canc protected abstract void CompleteAsyncMethodChecksBetweenStartAndFinish(); - public override ImmutableArray TypeParameters - { - get { return _typeParameters; } - } + public abstract override ImmutableArray TypeParameters { get; } public abstract override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterInfo.cs b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterInfo.cs new file mode 100644 index 0000000000000..7a91009a35962 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/TypeParameterInfo.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// Wrapper around type-parameter/constraints/constraint-kind info. We wrap this information (instead of inlining + /// directly within type/method symbols) as most types/methods are not generic. As such, all those non-generic + /// types can point at the singleton sentinel value, and avoid two pointers of overhead. + /// + internal sealed class TypeParameterInfo + { + public ImmutableArray LazyTypeParameters; + + /// + /// A collection of type parameter constraint types, populated when + /// constraint types for the first type parameter are requested. + /// + public ImmutableArray> LazyTypeParameterConstraintTypes; + + /// + /// A collection of type parameter constraint kinds, populated when + /// constraint kinds for the first type parameter are requested. + /// + public ImmutableArray LazyTypeParameterConstraintKinds; + + public static readonly TypeParameterInfo Empty = new TypeParameterInfo + { + LazyTypeParameters = ImmutableArray.Empty, + LazyTypeParameterConstraintTypes = ImmutableArray>.Empty, + LazyTypeParameterConstraintKinds = ImmutableArray.Empty, + }; + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs index c497dbc226869..f063d89aa6ca9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs @@ -39,7 +39,7 @@ protected SynthesizedRecordOrdinaryMethod(SourceMemberContainerTypeSymbol contai internal sealed override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.GetSynthesizedMemberKey(_memberOffset); - protected sealed override ImmutableArray MakeTypeParameters(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics) => ImmutableArray.Empty; + public sealed override ImmutableArray TypeParameters => ImmutableArray.Empty; public sealed override ImmutableArray> GetTypeParameterConstraintTypes() => ImmutableArray>.Empty;