File: Symbols\Source\SourceNamedTypeSymbol_Bases.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.
 
#nullable disable
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Collections;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    internal partial class SourceNamedTypeSymbol
    {
        private Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>> _lazyDeclaredBases;
 
        private NamedTypeSymbol _lazyBaseType = ErrorTypeSymbol.UnknownResultType;
        private ImmutableArray<NamedTypeSymbol> _lazyInterfaces;
 
        /// <summary>
        /// Gets the BaseType of this type. If the base type could not be determined, then 
        /// an instance of ErrorType is returned. If this kind of type does not have a base type
        /// (for example, interfaces), null is returned. Also the special class System.Object
        /// always has a BaseType of null.
        /// </summary>
        internal sealed override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics
        {
            get
            {
                if (ReferenceEquals(_lazyBaseType, ErrorTypeSymbol.UnknownResultType))
                {
                    // force resolution of bases in containing type
                    // to make base resolution errors more deterministic
                    if ((object)ContainingType != null &&
                        TypeKind is not (TypeKind.Enum or TypeKind.Delegate or TypeKind.Submission))
                    {
                        var tmp = ContainingType.BaseTypeNoUseSiteDiagnostics;
                    }
 
                    var diagnostics = BindingDiagnosticBag.GetInstance();
                    var acyclicBase = this.MakeAcyclicBaseType(diagnostics);
                    if (ReferenceEquals(Interlocked.CompareExchange(ref _lazyBaseType, acyclicBase, ErrorTypeSymbol.UnknownResultType), ErrorTypeSymbol.UnknownResultType))
                    {
                        AddDeclarationDiagnostics(diagnostics);
                    }
                    diagnostics.Free();
                }
 
                return _lazyBaseType;
            }
        }
 
        /// <summary>
        /// Gets the set of interfaces that this type directly implements. This set does not include
        /// interfaces that are base interfaces of directly implemented interfaces.
        /// </summary>
        internal sealed override ImmutableArray<NamedTypeSymbol> InterfacesNoUseSiteDiagnostics(ConsList<TypeSymbol> basesBeingResolved)
        {
            if (_lazyInterfaces.IsDefault)
            {
                if (basesBeingResolved != null && basesBeingResolved.ContainsReference(this.OriginalDefinition))
                {
                    return ImmutableArray<NamedTypeSymbol>.Empty;
                }
 
                var diagnostics = BindingDiagnosticBag.GetInstance();
                var acyclicInterfaces = MakeAcyclicInterfaces(basesBeingResolved, diagnostics);
                if (ImmutableInterlocked.InterlockedCompareExchange(ref _lazyInterfaces, acyclicInterfaces, default(ImmutableArray<NamedTypeSymbol>)).IsDefault)
                {
                    AddDeclarationDiagnostics(diagnostics);
                }
                diagnostics.Free();
            }
 
            return _lazyInterfaces;
        }
 
        protected override void CheckBase(BindingDiagnosticBag diagnostics)
        {
            var localBase = this.BaseTypeNoUseSiteDiagnostics;
 
            if ((object)localBase == null)
            {
                // nothing to verify
                return;
            }
 
            Location baseLocation = null;
            bool baseContainsErrorTypes = localBase.ContainsErrorType();
 
            if (!baseContainsErrorTypes)
            {
                baseLocation = FindBaseRefSyntax(localBase);
                Debug.Assert(!this.IsClassType() || localBase.IsObjectType() || baseLocation != null);
            }
 
            // you need to know all bases before you can ask this question... (asking this causes a cycle)
            if (this.IsGenericType && !baseContainsErrorTypes && this.DeclaringCompilation.IsAttributeType(localBase))
            {
                MessageID.IDS_FeatureGenericAttributes.CheckFeatureAvailability(diagnostics, this.DeclaringCompilation, baseLocation);
            }
 
            // Check constraints on the first declaration with explicit bases.
            var singleDeclaration = this.FirstDeclarationWithExplicitBases();
            if (singleDeclaration != null)
            {
                var corLibrary = this.ContainingAssembly.CorLibrary;
                var conversions = corLibrary.TypeConversions;
                var location = singleDeclaration.NameLocation;
 
                localBase.CheckAllConstraints(DeclaringCompilation, conversions, location, diagnostics);
            }
 
            // Records can only inherit from other records or object
            if (this.IsClassType() && !localBase.IsObjectType() && !baseContainsErrorTypes)
            {
                var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
 
                if (declaration.Kind == DeclarationKind.Record)
                {
                    if (SynthesizedRecordClone.FindValidCloneMethod(localBase, ref useSiteInfo) is null)
                    {
                        diagnostics.Add(ErrorCode.ERR_BadRecordBase, baseLocation);
                    }
                }
                else if (SynthesizedRecordClone.FindValidCloneMethod(localBase, ref useSiteInfo) is object)
                {
                    diagnostics.Add(ErrorCode.ERR_BadInheritanceFromRecord, baseLocation);
                }
 
                diagnostics.Add(baseLocation, useSiteInfo);
            }
        }
 
        protected override void CheckInterfaces(BindingDiagnosticBag diagnostics)
        {
            // Check declared interfaces and all base interfaces. This is necessary
            // since references to all interfaces will be emitted to metadata
            // and it's possible to define derived interfaces with weaker
            // constraints than the base interfaces, at least in metadata.
            var interfaces = this.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics;
 
            if (interfaces.IsEmpty)
            {
                // nothing to verify
                return;
            }
 
            // Check constraints on the first declaration with explicit bases.
            var singleDeclaration = this.FirstDeclarationWithExplicitBases();
            if (singleDeclaration != null)
            {
                var corLibrary = this.ContainingAssembly.CorLibrary;
                var conversions = corLibrary.TypeConversions;
                var location = singleDeclaration.NameLocation;
 
                foreach (var pair in interfaces)
                {
                    MultiDictionary<NamedTypeSymbol, NamedTypeSymbol>.ValueSet set = pair.Value;
 
                    foreach (var @interface in set)
                    {
                        @interface.CheckAllConstraints(DeclaringCompilation, conversions, location, diagnostics);
                    }
 
                    if (set.Count > 1)
                    {
                        NamedTypeSymbol other = pair.Key;
                        foreach (var @interface in set)
                        {
                            if ((object)other == @interface)
                            {
                                continue;
                            }
 
                            // InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics populates the set with interfaces that match by CLR signature.
                            Debug.Assert(!other.Equals(@interface, TypeCompareKind.ConsiderEverything));
                            Debug.Assert(other.Equals(@interface, TypeCompareKind.CLRSignatureCompareOptions));
 
                            if (other.Equals(@interface, TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
                            {
                                if (!other.Equals(@interface, TypeCompareKind.ObliviousNullableModifierMatchesAny))
                                {
                                    diagnostics.Add(ErrorCode.WRN_DuplicateInterfaceWithNullabilityMismatchInBaseList, location, @interface, this);
                                }
                            }
                            else if (other.Equals(@interface, TypeCompareKind.IgnoreTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
                            {
                                diagnostics.Add(ErrorCode.ERR_DuplicateInterfaceWithTupleNamesInBaseList, location, @interface, other, this);
                            }
                            else
                            {
                                diagnostics.Add(ErrorCode.ERR_DuplicateInterfaceWithDifferencesInBaseList, location, @interface, other, this);
                            }
                        }
                    }
                }
            }
        }
 
        // finds syntax location where given type was inherited
        // should be used for error reporting on unexpected inherited types.
        private SourceLocation FindBaseRefSyntax(NamedTypeSymbol baseSym)
        {
            foreach (var decl in this.declaration.Declarations)
            {
                BaseListSyntax bases = GetBaseListOpt(decl);
                if (bases != null)
                {
                    var baseBinder = this.DeclaringCompilation.GetBinder(bases);
                    // Wrap base binder in a location-specific binder that will avoid generic constraint checks.
                    baseBinder = baseBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this);
 
                    foreach (var baseTypeSyntax in bases.Types)
                    {
                        var b = baseTypeSyntax.Type;
                        var curBaseSym = baseBinder.BindType(b, BindingDiagnosticBag.Discarded).Type;
 
                        if (baseSym.Equals(curBaseSym))
                        {
                            return new SourceLocation(b);
                        }
                    }
                }
            }
 
            return null;
        }
 
        // Returns the first declaration in the merged declarations list that includes
        // base types or interfaces. Returns null if there are no such declarations.
        private SingleTypeDeclaration FirstDeclarationWithExplicitBases()
        {
            foreach (var singleDeclaration in this.declaration.Declarations)
            {
                var bases = GetBaseListOpt(singleDeclaration);
                if (bases != null)
                {
                    return singleDeclaration;
                }
            }
 
            return null;
        }
 
        internal Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>> GetDeclaredBases(ConsList<TypeSymbol> basesBeingResolved)
        {
            if (ReferenceEquals(_lazyDeclaredBases, null))
            {
                var diagnostics = BindingDiagnosticBag.GetInstance();
                if (Interlocked.CompareExchange(ref _lazyDeclaredBases, MakeDeclaredBases(basesBeingResolved, diagnostics), null) == null)
                {
                    AddDeclarationDiagnostics(diagnostics);
                }
                diagnostics.Free();
            }
 
            return _lazyDeclaredBases;
        }
 
        internal override NamedTypeSymbol GetDeclaredBaseType(ConsList<TypeSymbol> basesBeingResolved)
        {
            return GetDeclaredBases(basesBeingResolved).Item1;
        }
 
        internal override ImmutableArray<NamedTypeSymbol> GetDeclaredInterfaces(ConsList<TypeSymbol> basesBeingResolved)
        {
            return GetDeclaredBases(basesBeingResolved).Item2;
        }
 
        private Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>> MakeDeclaredBases(ConsList<TypeSymbol> basesBeingResolved, BindingDiagnosticBag diagnostics)
        {
            if (this.TypeKind == TypeKind.Enum)
            {
                // Handled by GetEnumUnderlyingType().
                return new Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>>(null, ImmutableArray<NamedTypeSymbol>.Empty);
            }
 
            var reportedPartialConflict = false;
            Debug.Assert(basesBeingResolved == null || !basesBeingResolved.ContainsReference(this.OriginalDefinition));
            var newBasesBeingResolved = basesBeingResolved.Prepend(this.OriginalDefinition);
            var baseInterfaces = ArrayBuilder<NamedTypeSymbol>.GetInstance();
 
            NamedTypeSymbol baseType = null;
            SourceLocation baseTypeLocation = null;
 
            var interfaceLocations = SpecializedSymbolCollections.GetPooledSymbolDictionaryInstance<NamedTypeSymbol, SourceLocation>();
 
            foreach (var decl in this.declaration.Declarations)
            {
                Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>> one = MakeOneDeclaredBases(newBasesBeingResolved, decl, diagnostics);
                if ((object)one == null) continue;
 
                var partBase = one.Item1;
                var partInterfaces = one.Item2;
                if (!reportedPartialConflict)
                {
                    if ((object)baseType == null)
                    {
                        baseType = partBase;
                        baseTypeLocation = decl.NameLocation;
                    }
                    else if (baseType.TypeKind == TypeKind.Error && (object)partBase != null)
                    {
                        // if the old base was an error symbol, copy it to the interfaces list so it doesn't get lost
                        partInterfaces = partInterfaces.Add(baseType);
                        baseType = partBase;
                        baseTypeLocation = decl.NameLocation;
                    }
                    else if ((object)partBase != null && !TypeSymbol.Equals(partBase, baseType, TypeCompareKind.ConsiderEverything) && partBase.TypeKind != TypeKind.Error)
                    {
                        // the parts do not agree
                        if (partBase.Equals(baseType, TypeCompareKind.ObliviousNullableModifierMatchesAny))
                        {
                            if (containsOnlyOblivious(baseType))
                            {
                                baseType = partBase;
                                baseTypeLocation = decl.NameLocation;
                            }
                            else if (!containsOnlyOblivious(partBase))
                            {
                                reportBaseType();
                            }
                        }
                        else
                        {
                            reportBaseType();
                        }
 
                        void reportBaseType()
                        {
                            var info = diagnostics.Add(ErrorCode.ERR_PartialMultipleBases, GetFirstLocation(), this);
                            baseType = new ExtendedErrorTypeSymbol(baseType, LookupResultKind.Ambiguous, info);
                            baseTypeLocation = decl.NameLocation;
                            reportedPartialConflict = true;
                        }
 
                        static bool containsOnlyOblivious(TypeSymbol type)
                        {
                            return TypeWithAnnotations.Create(type).VisitType(
                                type: null,
                                static (type, arg, flag) => !type.Type.IsValueType && !type.NullableAnnotation.IsOblivious(),
                                typePredicate: null,
                                arg: (object)null) is null;
                        }
                    }
                }
 
                foreach (var t in partInterfaces)
                {
                    if (!interfaceLocations.ContainsKey(t))
                    {
                        baseInterfaces.Add(t);
                        interfaceLocations.Add(t, decl.NameLocation);
                    }
                }
            }
 
            CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
 
            if (declaration.Kind is DeclarationKind.Record or DeclarationKind.RecordStruct)
            {
                var type = DeclaringCompilation.GetWellKnownType(WellKnownType.System_IEquatable_T).Construct(this);
                if (baseInterfaces.IndexOf(type, SymbolEqualityComparer.AllIgnoreOptions) < 0)
                {
                    baseInterfaces.Add(type);
                    type.AddUseSiteInfo(ref useSiteInfo);
                }
            }
 
            if ((object)baseType != null)
            {
                Debug.Assert(baseTypeLocation != null);
                if (baseType.IsStatic)
                {
                    // '{1}': cannot derive from static class '{0}'
                    diagnostics.Add(ErrorCode.ERR_StaticBaseClass, baseTypeLocation, baseType, this);
                }
 
                if (!this.IsNoMoreVisibleThan(baseType, ref useSiteInfo))
                {
                    // Inconsistent accessibility: base class '{1}' is less accessible than class '{0}'
                    diagnostics.Add(ErrorCode.ERR_BadVisBaseClass, baseTypeLocation, this, baseType);
                }
 
                if (baseType.HasFileLocalTypes() && !this.HasFileLocalTypes())
                {
                    diagnostics.Add(ErrorCode.ERR_FileTypeBase, baseTypeLocation, baseType, this);
                }
            }
 
            var baseInterfacesRO = baseInterfaces.ToImmutableAndFree();
            if (DeclaredAccessibility != Accessibility.Private && IsInterface)
            {
                foreach (var i in baseInterfacesRO)
                {
                    if (!i.IsAtLeastAsVisibleAs(this, ref useSiteInfo))
                    {
                        // Inconsistent accessibility: base interface '{1}' is less accessible than interface '{0}'
                        diagnostics.Add(ErrorCode.ERR_BadVisBaseInterface, interfaceLocations[i], this, i);
                    }
 
                    if (i.HasFileLocalTypes() && !this.HasFileLocalTypes())
                    {
                        diagnostics.Add(ErrorCode.ERR_FileTypeBase, interfaceLocations[i], i, this);
                    }
                }
            }
 
            interfaceLocations.Free();
 
            diagnostics.Add(GetFirstLocation(), useSiteInfo);
 
            return new Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>>(baseType, baseInterfacesRO);
        }
 
        private static BaseListSyntax GetBaseListOpt(SingleTypeDeclaration decl)
        {
            if (decl.HasBaseDeclarations)
            {
                var typeDeclaration = (BaseTypeDeclarationSyntax)decl.SyntaxReference.GetSyntax();
                return typeDeclaration.BaseList;
            }
 
            return null;
        }
 
        // process the base list for one part of a partial class, or for the only part of any other type declaration.
        private Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>> MakeOneDeclaredBases(ConsList<TypeSymbol> newBasesBeingResolved, SingleTypeDeclaration decl, BindingDiagnosticBag diagnostics)
        {
            BaseListSyntax bases = GetBaseListOpt(decl);
            if (bases == null)
            {
                return null;
            }
 
            NamedTypeSymbol localBase = null;
            var localInterfaces = ArrayBuilder<NamedTypeSymbol>.GetInstance();
            var baseBinder = this.DeclaringCompilation.GetBinder(bases);
 
            // Wrap base binder in a location-specific binder that will avoid generic constraint checks
            // (to avoid cycles if the constraint types are not bound yet). Instead, constraint checks
            // are handled by the caller.
            baseBinder = baseBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this);
 
            int i = -1;
            foreach (var baseTypeSyntax in bases.Types)
            {
                i++;
                var typeSyntax = baseTypeSyntax.Type;
                if (typeSyntax.Kind() != SyntaxKind.PredefinedType && !SyntaxFacts.IsName(typeSyntax.Kind()))
                {
                    diagnostics.Add(ErrorCode.ERR_BadBaseType, typeSyntax.GetLocation());
                }
 
                var location = new SourceLocation(typeSyntax);
 
                TypeSymbol baseType;
 
                if (i == 0 && TypeKind == TypeKind.Class) // allow class in the first position
                {
                    baseType = baseBinder.BindType(typeSyntax, diagnostics, newBasesBeingResolved).Type;
 
                    SpecialType baseSpecialType = baseType.SpecialType;
                    if (IsRestrictedBaseType(baseSpecialType))
                    {
                        // check for one of the specific exceptions required for compiling mscorlib
                        if (this.SpecialType == SpecialType.System_Enum && baseSpecialType == SpecialType.System_ValueType ||
                            this.SpecialType == SpecialType.System_MulticastDelegate && baseSpecialType == SpecialType.System_Delegate)
                        {
                            // allowed
                        }
                        else if (baseSpecialType == SpecialType.System_Array && this.ContainingAssembly.CorLibrary == this.ContainingAssembly)
                        {
                            // Specific exception for System.ArrayContracts, which is only built when CONTRACTS_FULL is defined.
                            // (See InheritanceResolver::CheckForBaseClassErrors).
                        }
                        else
                        {
                            // '{0}' cannot derive from special class '{1}'
                            diagnostics.Add(ErrorCode.ERR_DeriveFromEnumOrValueType, location, this, baseType);
                            continue;
                        }
                    }
 
                    if (baseType.IsSealed && !this.IsStatic) // Give precedence to ERR_StaticDerivedFromNonObject
                    {
                        diagnostics.Add(ErrorCode.ERR_CantDeriveFromSealedType, location, this, baseType);
                        continue;
                    }
 
                    bool baseTypeIsErrorWithoutInterfaceGuess = false;
 
                    // If baseType is an error symbol and our best guess is that the desired symbol
                    // is an interface, then put baseType in the interfaces list, rather than the
                    // base type slot, to avoid the frustrating scenario where an error message
                    // indicates that the symbol being returned as the base type was elsewhere
                    // interpreted as an interface.
                    if (baseType.TypeKind == TypeKind.Error)
                    {
                        baseTypeIsErrorWithoutInterfaceGuess = true;
 
                        TypeKind guessTypeKind = baseType.GetNonErrorTypeKindGuess();
                        if (guessTypeKind == TypeKind.Interface)
                        {
                            //base type is an error *with* a guessed interface
                            baseTypeIsErrorWithoutInterfaceGuess = false;
                        }
                    }
 
                    if ((baseType.TypeKind == TypeKind.Class ||
                         baseType.TypeKind == TypeKind.Delegate ||
                         baseType.TypeKind == TypeKind.Struct ||
                         baseTypeIsErrorWithoutInterfaceGuess) &&
                        ((object)localBase == null))
                    {
                        localBase = (NamedTypeSymbol)baseType;
                        Debug.Assert((object)localBase != null);
                        if (this.IsStatic && localBase.SpecialType != SpecialType.System_Object)
                        {
                            // Static class '{0}' cannot derive from type '{1}'. Static classes must derive from object.
                            var info = diagnostics.Add(ErrorCode.ERR_StaticDerivedFromNonObject, location, this, localBase);
                            localBase = new ExtendedErrorTypeSymbol(localBase, LookupResultKind.NotReferencable, info);
                        }
                        checkPrimaryConstructorBaseType(baseTypeSyntax, localBase);
                        continue;
                    }
                }
                else
                {
                    baseType = baseBinder.BindType(typeSyntax, diagnostics, newBasesBeingResolved).Type;
                }
 
                if (i == 0)
                {
                    checkPrimaryConstructorBaseType(baseTypeSyntax, baseType);
                }
 
                switch (baseType.TypeKind)
                {
                    case TypeKind.Interface:
                        foreach (var t in localInterfaces)
                        {
                            if (t.Equals(baseType, TypeCompareKind.ConsiderEverything))
                            {
                                diagnostics.Add(ErrorCode.ERR_DuplicateInterfaceInBaseList, location, baseType);
                            }
                            else if (t.Equals(baseType, TypeCompareKind.ObliviousNullableModifierMatchesAny))
                            {
                                // duplicates with ?/! differences are reported later, we report local differences between oblivious and ?/! here
                                diagnostics.Add(ErrorCode.WRN_DuplicateInterfaceWithNullabilityMismatchInBaseList, location, baseType, this);
                            }
                        }
 
                        if (this.IsStatic)
                        {
                            // '{0}': static classes cannot implement interfaces
                            diagnostics.Add(ErrorCode.ERR_StaticClassInterfaceImpl, location, this);
                        }
 
                        if (this.IsRefLikeType)
                        {
                            Binder.CheckFeatureAvailability(typeSyntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics);
                        }
 
                        if (baseType.ContainsDynamic())
                        {
                            diagnostics.Add(ErrorCode.ERR_DeriveFromConstructedDynamic, location, this, baseType);
                        }
 
                        localInterfaces.Add((NamedTypeSymbol)baseType);
                        continue;
 
                    case TypeKind.Class:
                        if (TypeKind == TypeKind.Class)
                        {
                            if ((object)localBase == null)
                            {
                                localBase = (NamedTypeSymbol)baseType;
                                diagnostics.Add(ErrorCode.ERR_BaseClassMustBeFirst, location, baseType);
                                continue;
                            }
                            else
                            {
                                diagnostics.Add(ErrorCode.ERR_NoMultipleInheritance, location, this, localBase, baseType);
                                continue;
                            }
                        }
                        goto default;
 
                    case TypeKind.TypeParameter:
                        diagnostics.Add(ErrorCode.ERR_DerivingFromATyVar, location, baseType);
                        continue;
 
                    case TypeKind.Error:
                        // put the error type in the interface list so we don't lose track of it
                        localInterfaces.Add((NamedTypeSymbol)baseType);
                        continue;
 
                    case TypeKind.Dynamic:
                        diagnostics.Add(ErrorCode.ERR_DeriveFromDynamic, location, this);
                        continue;
 
                    case TypeKind.Submission:
                        throw ExceptionUtilities.UnexpectedValue(baseType.TypeKind);
 
                    default:
                        diagnostics.Add(ErrorCode.ERR_NonInterfaceInInterfaceList, location, baseType);
                        continue;
                }
            }
 
            if (this.SpecialType == SpecialType.System_Object && ((object)localBase != null || localInterfaces.Count != 0))
            {
                var name = GetName(bases.Parent);
                diagnostics.Add(ErrorCode.ERR_ObjectCantHaveBases, new SourceLocation(name));
            }
 
            return new Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>>(localBase, localInterfaces.ToImmutableAndFree());
 
            void checkPrimaryConstructorBaseType(BaseTypeSyntax baseTypeSyntax, TypeSymbol baseType)
            {
                if (baseTypeSyntax is PrimaryConstructorBaseTypeSyntax primaryConstructorBaseType &&
                    (TypeKind != TypeKind.Class || baseType.TypeKind == TypeKind.Interface || ((TypeDeclarationSyntax)decl.SyntaxReference.GetSyntax()).ParameterList is null))
                {
                    diagnostics.Add(ErrorCode.ERR_UnexpectedArgumentList, primaryConstructorBaseType.ArgumentList.Location);
                }
            }
        }
 
        /// <summary>
        /// Returns true if the type cannot be used as an explicit base class.
        /// </summary>
        private static bool IsRestrictedBaseType(SpecialType specialType)
        {
            switch (specialType)
            {
                case SpecialType.System_Array:
                case SpecialType.System_Enum:
                case SpecialType.System_Delegate:
                case SpecialType.System_MulticastDelegate:
                case SpecialType.System_ValueType:
                    return true;
            }
 
            return false;
        }
 
        private ImmutableArray<NamedTypeSymbol> MakeAcyclicInterfaces(ConsList<TypeSymbol> basesBeingResolved, BindingDiagnosticBag diagnostics)
        {
            var typeKind = this.TypeKind;
 
            if (typeKind == TypeKind.Enum)
            {
                Debug.Assert(GetDeclaredInterfaces(basesBeingResolved: null).IsEmpty, "Computation skipped for enums");
                return ImmutableArray<NamedTypeSymbol>.Empty;
            }
 
            var declaredInterfaces = GetDeclaredInterfaces(basesBeingResolved: basesBeingResolved);
            bool isInterface = (typeKind == TypeKind.Interface);
 
            ArrayBuilder<NamedTypeSymbol> result = isInterface ? ArrayBuilder<NamedTypeSymbol>.GetInstance() : null;
            foreach (var t in declaredInterfaces)
            {
                if (isInterface)
                {
                    if (BaseTypeAnalysis.TypeDependsOn(depends: t, on: this))
                    {
                        result.Add(new ExtendedErrorTypeSymbol(t, LookupResultKind.NotReferencable,
                            diagnostics.Add(ErrorCode.ERR_CycleInInterfaceInheritance, GetFirstLocation(), this, t)));
                        continue;
                    }
                    else
                    {
                        result.Add(t);
                    }
                }
 
                var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
 
                if (t.DeclaringCompilation != this.DeclaringCompilation)
                {
                    t.AddUseSiteInfo(ref useSiteInfo);
 
                    foreach (var @interface in t.AllInterfacesNoUseSiteDiagnostics)
                    {
                        if (@interface.DeclaringCompilation != this.DeclaringCompilation)
                        {
                            @interface.AddUseSiteInfo(ref useSiteInfo);
                        }
                    }
                }
 
                diagnostics.Add(GetFirstLocation(), useSiteInfo);
            }
 
            return isInterface ? result.ToImmutableAndFree() : declaredInterfaces;
        }
 
        private NamedTypeSymbol MakeAcyclicBaseType(BindingDiagnosticBag diagnostics)
        {
            var typeKind = this.TypeKind;
            var compilation = this.DeclaringCompilation;
            NamedTypeSymbol declaredBase;
            if (typeKind == TypeKind.Enum)
            {
                Debug.Assert((object)GetDeclaredBaseType(basesBeingResolved: null) == null, "Computation skipped for enums");
                declaredBase = compilation.GetSpecialType(SpecialType.System_Enum);
            }
            else
            {
                declaredBase = GetDeclaredBaseType(basesBeingResolved: null);
            }
 
            if ((object)declaredBase == null)
            {
                switch (typeKind)
                {
                    case TypeKind.Class:
 
                        if (this.SpecialType == SpecialType.System_Object)
                        {
                            return null;
                        }
 
                        declaredBase = compilation.GetSpecialType(SpecialType.System_Object);
                        break;
 
                    case TypeKind.Struct:
                        declaredBase = compilation.GetSpecialType(SpecialType.System_ValueType);
                        break;
 
                    case TypeKind.Interface:
                        return null;
 
                    case TypeKind.Delegate:
                        declaredBase = compilation.GetSpecialType(SpecialType.System_MulticastDelegate);
                        break;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(typeKind);
                }
            }
 
            if (BaseTypeAnalysis.TypeDependsOn(declaredBase, this))
            {
                return new ExtendedErrorTypeSymbol(declaredBase, LookupResultKind.NotReferencable,
                    diagnostics.Add(ErrorCode.ERR_CircularBase, GetFirstLocation(), declaredBase, this));
            }
 
            this.SetKnownToHaveNoDeclaredBaseCycles();
 
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
            NamedTypeSymbol current = declaredBase;
 
            do
            {
                if (current.DeclaringCompilation == this.DeclaringCompilation)
                {
                    break;
                }
 
                current.AddUseSiteInfo(ref useSiteInfo);
                current = current.BaseTypeNoUseSiteDiagnostics;
            }
            while ((object)current != null);
 
            diagnostics.Add(useSiteInfo.Diagnostics.IsNullOrEmpty() ? Location.None : (FindBaseRefSyntax(declaredBase) ?? GetFirstLocation()), useSiteInfo);
 
            return declaredBase;
        }
    }
}