File: Symbols\FunctionTypeSymbol.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// Inferred delegate type state, recorded during testing only.
    /// </summary>
    internal sealed class InferredDelegateTypeData
    {
        /// <summary>
        /// Number of delegate types calculated in the compilation.
        /// </summary>
        internal int InferredDelegateCount;
    }
 
    /// <summary>
    /// A <see cref="TypeSymbol"/> implementation that represents the lazily-inferred signature of a
    /// lambda expression or method group. This is implemented as a <see cref="TypeSymbol"/>
    /// to allow types and function signatures to be treated similarly in <see cref="ConversionsBase"/>,
    /// <see cref="BestTypeInferrer"/>, and <see cref="MethodTypeInferrer"/>. Instances of this type
    /// should only be used in those code paths and should not be exposed from the symbol model.
    /// The actual delegate signature is calculated on demand in <see cref="GetInternalDelegateType()"/>.
    /// </summary>
    [DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
    internal sealed class FunctionTypeSymbol : TypeSymbol
    {
        private static readonly NamedTypeSymbol Uninitialized = new UnsupportedMetadataTypeSymbol();
 
        private readonly Binder? _binder;
        private readonly Func<Binder, BoundExpression, NamedTypeSymbol?>? _calculateDelegate;
 
        private BoundExpression? _expression;
        private NamedTypeSymbol? _lazyDelegateType;
 
        internal static FunctionTypeSymbol? CreateIfFeatureEnabled(SyntaxNode syntax, Binder binder, Func<Binder, BoundExpression, NamedTypeSymbol?> calculateDelegate)
        {
            return syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType) ?
                new FunctionTypeSymbol(binder, calculateDelegate) :
                null;
        }
 
        private FunctionTypeSymbol(Binder binder, Func<Binder, BoundExpression, NamedTypeSymbol?> calculateDelegate)
        {
            _binder = binder;
            _calculateDelegate = calculateDelegate;
            _lazyDelegateType = Uninitialized;
        }
 
        internal FunctionTypeSymbol(NamedTypeSymbol delegateType)
        {
            _lazyDelegateType = delegateType;
        }
 
        internal void SetExpression(BoundExpression expression)
        {
            Debug.Assert((object?)_lazyDelegateType == Uninitialized);
            Debug.Assert(_expression is null);
            Debug.Assert(expression.Kind is BoundKind.MethodGroup or BoundKind.UnboundLambda);
 
            _expression = expression;
        }
 
        /// <summary>
        /// Returns the inferred signature as a delegate type
        /// or null if the signature could not be inferred.
        /// </summary>
        internal NamedTypeSymbol? GetInternalDelegateType()
        {
            if ((object?)_lazyDelegateType == Uninitialized)
            {
                Debug.Assert(_binder is { });
                Debug.Assert(_calculateDelegate is { });
                Debug.Assert(_expression is { });
 
                var delegateType = _calculateDelegate(_binder, _expression);
                var result = Interlocked.CompareExchange(ref _lazyDelegateType, delegateType, Uninitialized);
 
                if (_binder.Compilation.TestOnlyCompilationData is InferredDelegateTypeData data &&
                    (object?)result == Uninitialized)
                {
                    Interlocked.Increment(ref data.InferredDelegateCount);
                }
            }
 
            return _lazyDelegateType;
        }
 
        public override bool IsReferenceType => true;
 
        public override bool IsValueType => false;
 
        public override TypeKind TypeKind => TypeKindInternal.FunctionType;
 
        public override bool IsRefLikeType => false;
 
        public override bool IsReadOnly => true;
 
        public override SymbolKind Kind => SymbolKindInternal.FunctionType;
 
        public override Symbol? ContainingSymbol => null;
 
        public override ImmutableArray<Location> Locations => throw ExceptionUtilities.Unreachable();
 
        public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences => throw ExceptionUtilities.Unreachable();
 
        public override Accessibility DeclaredAccessibility => throw ExceptionUtilities.Unreachable();
 
        public override bool IsStatic => false;
 
        public override bool IsAbstract => throw ExceptionUtilities.Unreachable();
 
        public override bool IsSealed => throw ExceptionUtilities.Unreachable();
 
        internal override NamedTypeSymbol? BaseTypeNoUseSiteDiagnostics => null;
 
        internal override bool IsRecord => throw ExceptionUtilities.Unreachable();
 
        internal override bool IsRecordStruct => throw ExceptionUtilities.Unreachable();
 
        internal override ObsoleteAttributeData ObsoleteAttributeData => throw ExceptionUtilities.Unreachable();
 
        public override void Accept(CSharpSymbolVisitor visitor) => throw ExceptionUtilities.Unreachable();
 
        public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor) => throw ExceptionUtilities.Unreachable();
 
        public override ImmutableArray<Symbol> GetMembers() => throw ExceptionUtilities.Unreachable();
 
        public override ImmutableArray<Symbol> GetMembers(string name) => throw ExceptionUtilities.Unreachable();
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers() => throw ExceptionUtilities.Unreachable();
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name) => throw ExceptionUtilities.Unreachable();
 
        protected override ISymbol CreateISymbol() => throw ExceptionUtilities.Unreachable();
 
        protected override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation) => throw ExceptionUtilities.Unreachable();
 
        internal override TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument a) => throw ExceptionUtilities.Unreachable();
 
        internal override void AddNullableTransforms(ArrayBuilder<byte> transforms) => throw ExceptionUtilities.Unreachable();
 
        internal override bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray<byte> transforms, ref int position, out TypeSymbol result) => throw ExceptionUtilities.Unreachable();
 
        internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) => throw ExceptionUtilities.Unreachable();
 
        internal override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet<TypeSymbol> checkedTypes) => throw ExceptionUtilities.Unreachable();
 
        internal override ImmutableArray<NamedTypeSymbol> InterfacesNoUseSiteDiagnostics(ConsList<TypeSymbol>? basesBeingResolved = null) => ImmutableArray<NamedTypeSymbol>.Empty;
 
        internal override TypeSymbol MergeEquivalentTypes(TypeSymbol other, VarianceKind variance)
        {
            Debug.Assert(this.Equals(other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes));
 
            var thisDelegateType = GetInternalDelegateType();
            var otherType = (FunctionTypeSymbol)other;
            var otherDelegateType = otherType.GetInternalDelegateType();
 
            if (thisDelegateType is null || otherDelegateType is null)
            {
                return this;
            }
 
            var mergedDelegateType = (NamedTypeSymbol)thisDelegateType.MergeEquivalentTypes(otherDelegateType, variance);
            return (object)thisDelegateType == mergedDelegateType ?
                this :
                otherType.WithDelegateType(mergedDelegateType);
        }
 
        internal override TypeSymbol SetNullabilityForReferenceTypes(Func<TypeWithAnnotations, TypeWithAnnotations> transform)
        {
            var thisDelegateType = GetInternalDelegateType();
            if (thisDelegateType is null)
            {
                return this;
            }
            return WithDelegateType((NamedTypeSymbol)thisDelegateType.SetNullabilityForReferenceTypes(transform));
        }
 
        private FunctionTypeSymbol WithDelegateType(NamedTypeSymbol delegateType)
        {
            var thisDelegateType = GetInternalDelegateType();
            return (object?)thisDelegateType == delegateType ?
                this :
                new FunctionTypeSymbol(delegateType);
        }
 
        internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() => throw ExceptionUtilities.Unreachable();
 
        internal override bool HasInlineArrayAttribute(out int length)
        {
            length = 0;
            return false;
        }
 
        internal override bool Equals(TypeSymbol t2, TypeCompareKind compareKind)
        {
            if (ReferenceEquals(this, t2))
            {
                return true;
            }
 
            if (t2 is FunctionTypeSymbol otherType)
            {
                var thisDelegateType = GetInternalDelegateType();
                var otherDelegateType = otherType.GetInternalDelegateType();
 
                if (thisDelegateType is null || otherDelegateType is null)
                {
                    return false;
                }
 
                return Equals(thisDelegateType, otherDelegateType, compareKind);
            }
 
            return false;
        }
 
        public override int GetHashCode()
        {
            return GetInternalDelegateType()?.GetHashCode() ?? 0;
        }
 
        internal override string GetDebuggerDisplay()
        {
            return $"FunctionTypeSymbol: {GetInternalDelegateType()?.GetDebuggerDisplay()}";
        }
    }
}