File: Binder\Binder_Constraints.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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    internal partial class Binder
    {
        /// <summary>
        /// Return a collection of bound constraint clauses indexed by type parameter
        /// ordinal. All constraint clauses are bound, even if there are multiple constraints
        /// for the same type parameter, or constraints for unrecognized type parameters.
        /// Extra constraints are not included in the returned collection however.
        /// </summary>
        internal ImmutableArray<TypeParameterConstraintClause> BindTypeParameterConstraintClauses(
            Symbol containingSymbol,
            ImmutableArray<TypeParameterSymbol> typeParameters,
            TypeParameterListSyntax typeParameterList,
            SyntaxList<TypeParameterConstraintClauseSyntax> clauses,
            BindingDiagnosticBag diagnostics,
            bool performOnlyCycleSafeValidation,
            bool isForOverride = false)
        {
            Debug.Assert(this.Flags.Includes(BinderFlags.GenericConstraintsClause));
            RoslynDebug.Assert((object)containingSymbol != null);
            Debug.Assert((containingSymbol.Kind == SymbolKind.NamedType) || (containingSymbol.Kind == SymbolKind.Method));
            Debug.Assert(typeParameters.Length > 0);
            Debug.Assert(clauses.Count > 0);
 
            int n = typeParameters.Length;
 
            // Create a map from type parameter name to ordinal.
            // No need to report duplicate names since duplicates
            // are reported when the type parameters are bound.
            var names = new Dictionary<string, int>(n, StringOrdinalComparer.Instance);
            foreach (var typeParameter in typeParameters)
            {
                var name = typeParameter.Name;
                if (!names.ContainsKey(name))
                {
                    names.Add(name, names.Count);
                }
            }
 
            // An array of constraint clauses, one for each type parameter, indexed by ordinal.
            var results = ArrayBuilder<TypeParameterConstraintClause?>.GetInstance(n, fillWithValue: null);
            var syntaxNodes = ArrayBuilder<ArrayBuilder<TypeConstraintSyntax>?>.GetInstance(n, fillWithValue: null);
 
            // Bind each clause and add to the results.
            foreach (var clause in clauses)
            {
                var name = clause.Name.Identifier.ValueText;
                RoslynDebug.Assert(name is object);
                int ordinal;
                if (names.TryGetValue(name, out ordinal))
                {
                    Debug.Assert(ordinal >= 0);
                    Debug.Assert(ordinal < n);
 
                    (TypeParameterConstraintClause constraintClause, ArrayBuilder<TypeConstraintSyntax>? typeConstraintNodes) = this.BindTypeParameterConstraints(typeParameterList.Parameters[ordinal], clause, isForOverride, diagnostics);
                    if (results[ordinal] == null)
                    {
                        results[ordinal] = constraintClause;
                        syntaxNodes[ordinal] = typeConstraintNodes;
                    }
                    else
                    {
                        // "A constraint clause has already been specified for type parameter '{0}'. ..."
                        diagnostics.Add(ErrorCode.ERR_DuplicateConstraintClause, clause.Name.Location, name);
                        typeConstraintNodes?.Free();
                    }
                }
                else
                {
                    // Unrecognized type parameter. Don't bother binding the constraints
                    // (the ": I<U>" in "where U : I<U>") since that will lead to additional
                    // errors ("type or namespace 'U' could not be found") if the type
                    // parameter is referenced in the constraints.
 
                    // "'{1}' does not define type parameter '{0}'"
                    diagnostics.Add(ErrorCode.ERR_TyVarNotFoundInConstraint, clause.Name.Location, name, containingSymbol.ConstructedFrom());
                }
            }
 
            // Add default values for type parameters without constraint clauses.
            for (int i = 0; i < n; i++)
            {
                if (results[i] == null)
                {
                    results[i] = GetDefaultTypeParameterConstraintClause(typeParameterList.Parameters[i], isForOverride);
                }
            }
 
            RemoveInvalidConstraints(typeParameters, results!, syntaxNodes, performOnlyCycleSafeValidation, diagnostics);
 
            foreach (var typeConstraintsSyntaxes in syntaxNodes)
            {
                typeConstraintsSyntaxes?.Free();
            }
 
            syntaxNodes.Free();
 
            return results.ToImmutableAndFree()!;
        }
 
        /// <summary>
        /// Bind and return a single type parameter constraint clause along with syntax nodes corresponding to type constraints.
        /// </summary>
        private (TypeParameterConstraintClause, ArrayBuilder<TypeConstraintSyntax>?) BindTypeParameterConstraints(
            TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, bool isForOverride, BindingDiagnosticBag diagnostics)
        {
            var constraints = TypeParameterConstraintKind.None;
            ArrayBuilder<TypeWithAnnotations>? constraintTypes = null;
            ArrayBuilder<TypeConstraintSyntax>? syntaxBuilder = null;
            SeparatedSyntaxList<TypeParameterConstraintSyntax> constraintsSyntax = constraintClauseSyntax.Constraints;
            Debug.Assert(!InExecutableBinder); // Cannot eagerly report diagnostics handled by LazyMissingNonNullTypesContextDiagnosticInfo 
            bool hasTypeLikeConstraint = false;
            bool reportedOverrideWithConstraints = false;
 
            for (int i = 0, n = constraintsSyntax.Count; i < n; i++)
            {
                var syntax = constraintsSyntax[i];
                switch (syntax.Kind())
                {
                    case SyntaxKind.ClassConstraint:
                        hasTypeLikeConstraint = true;
 
                        if (i != 0)
                        {
                            if (!reportedOverrideWithConstraints)
                            {
                                reportTypeConstraintsMustBeUniqueAndFirst(syntax, diagnostics);
                            }
 
                            if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0)
                            {
                                continue;
                            }
                        }
 
                        var constraintSyntax = (ClassOrStructConstraintSyntax)syntax;
                        SyntaxToken questionToken = constraintSyntax.QuestionToken;
                        if (questionToken.IsKind(SyntaxKind.QuestionToken))
                        {
                            constraints |= TypeParameterConstraintKind.NullableReferenceType;
 
                            if (isForOverride)
                            {
                                reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics);
                            }
                            else if (diagnostics.DiagnosticBag is DiagnosticBag diagnosticBag)
                            {
                                LazyMissingNonNullTypesContextDiagnosticInfo.AddAll(this, questionToken, type: null, diagnosticBag);
                            }
                        }
                        else if (isForOverride || AreNullableAnnotationsEnabled(constraintSyntax.ClassOrStructKeyword))
                        {
                            constraints |= TypeParameterConstraintKind.NotNullableReferenceType;
                        }
                        else
                        {
                            constraints |= TypeParameterConstraintKind.ReferenceType;
                        }
 
                        continue;
                    case SyntaxKind.StructConstraint:
                        hasTypeLikeConstraint = true;
 
                        if (i != 0)
                        {
                            if (!reportedOverrideWithConstraints)
                            {
                                reportTypeConstraintsMustBeUniqueAndFirst(syntax, diagnostics);
                            }
 
                            if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0)
                            {
                                continue;
                            }
                        }
 
                        constraints |= TypeParameterConstraintKind.ValueType;
                        continue;
                    case SyntaxKind.ConstructorConstraint:
                        if (isForOverride)
                        {
                            reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics);
                            continue;
                        }
 
                        if ((constraints & TypeParameterConstraintKind.ValueType) != 0)
                        {
                            diagnostics.Add(ErrorCode.ERR_NewBoundWithVal, syntax.GetFirstToken().GetLocation());
                        }
                        if ((constraints & TypeParameterConstraintKind.Unmanaged) != 0)
                        {
                            diagnostics.Add(ErrorCode.ERR_NewBoundWithUnmanaged, syntax.GetFirstToken().GetLocation());
                        }
 
                        if (i != n - 1 && constraintsSyntax[i + 1].Kind() != SyntaxKind.AllowsConstraintClause)
                        {
                            diagnostics.Add(ErrorCode.ERR_NewBoundMustBeLast, syntax.GetFirstToken().GetLocation());
                        }
 
                        constraints |= TypeParameterConstraintKind.Constructor;
                        continue;
                    case SyntaxKind.DefaultConstraint:
                        CheckFeatureAvailability(syntax, MessageID.IDS_FeatureDefaultTypeParameterConstraint, diagnostics);
 
                        if (!isForOverride)
                        {
                            diagnostics.Add(ErrorCode.ERR_DefaultConstraintOverrideOnly, syntax.GetLocation());
                        }
 
                        if (i != 0)
                        {
                            if (!reportedOverrideWithConstraints)
                            {
                                reportTypeConstraintsMustBeUniqueAndFirst(syntax, diagnostics);
                            }
 
                            if (isForOverride && (constraints & (TypeParameterConstraintKind.ValueType | TypeParameterConstraintKind.ReferenceType)) != 0)
                            {
                                continue;
                            }
                        }
 
                        constraints |= TypeParameterConstraintKind.Default;
                        continue;
                    case SyntaxKind.TypeConstraint:
                        if (isForOverride)
                        {
                            reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics);
                        }
                        else
                        {
                            hasTypeLikeConstraint = true;
 
                            if (constraintTypes == null)
                            {
                                constraintTypes = ArrayBuilder<TypeWithAnnotations>.GetInstance();
                                syntaxBuilder = ArrayBuilder<TypeConstraintSyntax>.GetInstance();
                            }
 
                            var typeConstraintSyntax = (TypeConstraintSyntax)syntax;
                            var typeSyntax = typeConstraintSyntax.Type;
 
                            var type = BindTypeOrConstraintKeyword(typeSyntax, diagnostics, out ConstraintContextualKeyword keyword);
 
                            switch (keyword)
                            {
                                case ConstraintContextualKeyword.Unmanaged:
                                    if (i != 0)
                                    {
                                        reportTypeConstraintsMustBeUniqueAndFirst(typeSyntax, diagnostics);
                                        continue;
                                    }
 
                                    // This should produce diagnostics if the types are missing
                                    GetWellKnownType(WellKnownType.System_Runtime_InteropServices_UnmanagedType, diagnostics, typeSyntax);
                                    GetSpecialType(SpecialType.System_ValueType, diagnostics, typeSyntax);
 
                                    constraints |= TypeParameterConstraintKind.Unmanaged;
                                    continue;
 
                                case ConstraintContextualKeyword.NotNull:
                                    if (i != 0)
                                    {
                                        reportTypeConstraintsMustBeUniqueAndFirst(typeSyntax, diagnostics);
                                    }
 
                                    constraints |= TypeParameterConstraintKind.NotNull;
                                    continue;
 
                                case ConstraintContextualKeyword.None:
                                    break;
                                default:
                                    throw ExceptionUtilities.UnexpectedValue(keyword);
                            }
 
                            constraintTypes.Add(type);
                            syntaxBuilder!.Add(typeConstraintSyntax);
                        }
                        continue;
 
                    case SyntaxKind.AllowsConstraintClause:
 
                        if (isForOverride)
                        {
                            reportOverrideWithConstraints(ref reportedOverrideWithConstraints, syntax, diagnostics);
                            continue;
                        }
 
                        if (i != n - 1)
                        {
                            diagnostics.Add(ErrorCode.ERR_AllowsClauseMustBeLast, syntax.GetFirstToken().GetLocation());
                        }
 
                        bool hasRefStructConstraint = false;
 
                        foreach (var allowsConstraint in ((AllowsConstraintClauseSyntax)syntax).Constraints)
                        {
                            if (allowsConstraint.Kind() == SyntaxKind.RefStructConstraint)
                            {
                                if (hasRefStructConstraint)
                                {
                                    diagnostics.Add(ErrorCode.ERR_RefStructConstraintAlreadySpecified, allowsConstraint);
                                }
                                else
                                {
                                    CheckFeatureAvailability(allowsConstraint, MessageID.IDS_FeatureAllowsRefStructConstraint, diagnostics);
 
                                    if (!Compilation.Assembly.RuntimeSupportsByRefLikeGenerics)
                                    {
                                        Error(diagnostics, ErrorCode.ERR_RuntimeDoesNotSupportByRefLikeGenerics, allowsConstraint);
                                    }
 
                                    constraints |= TypeParameterConstraintKind.AllowByRefLike;
                                    hasRefStructConstraint = true;
                                }
                            }
                        }
 
                        continue;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(syntax.Kind());
                }
            }
 
            if (!isForOverride && !hasTypeLikeConstraint && !AreNullableAnnotationsEnabled(typeParameterSyntax.Identifier))
            {
                constraints |= TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType;
            }
 
            Debug.Assert(!isForOverride ||
                         (constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType));
 
            return (TypeParameterConstraintClause.Create(constraints, constraintTypes?.ToImmutableAndFree() ?? ImmutableArray<TypeWithAnnotations>.Empty), syntaxBuilder);
 
            static void reportOverrideWithConstraints(ref bool reportedOverrideWithConstraints, TypeParameterConstraintSyntax syntax, BindingDiagnosticBag diagnostics)
            {
                if (!reportedOverrideWithConstraints)
                {
                    diagnostics.Add(ErrorCode.ERR_OverrideWithConstraints, syntax.GetLocation());
                    reportedOverrideWithConstraints = true;
                }
            }
 
            static void reportTypeConstraintsMustBeUniqueAndFirst(CSharpSyntaxNode syntax, BindingDiagnosticBag diagnostics)
            {
                diagnostics.Add(ErrorCode.ERR_TypeConstraintsMustBeUniqueAndFirst, syntax.GetLocation());
            }
        }
 
        internal ImmutableArray<TypeParameterConstraintClause> GetDefaultTypeParameterConstraintClauses(TypeParameterListSyntax typeParameterList)
        {
            var builder = ArrayBuilder<TypeParameterConstraintClause>.GetInstance(typeParameterList.Parameters.Count);
 
            foreach (TypeParameterSyntax typeParameterSyntax in typeParameterList.Parameters)
            {
                builder.Add(GetDefaultTypeParameterConstraintClause(typeParameterSyntax));
            }
 
            return builder.ToImmutableAndFree();
        }
 
        private TypeParameterConstraintClause GetDefaultTypeParameterConstraintClause(TypeParameterSyntax typeParameterSyntax, bool isForOverride = false)
        {
            return isForOverride || AreNullableAnnotationsEnabled(typeParameterSyntax.Identifier) ? TypeParameterConstraintClause.Empty : TypeParameterConstraintClause.ObliviousNullabilityIfReferenceType;
        }
 
        /// <summary>
        /// Constraints are checked for invalid types, duplicate types, and accessibility. 
        /// </summary>
        private static void RemoveInvalidConstraints(
            ImmutableArray<TypeParameterSymbol> typeParameters,
            ArrayBuilder<TypeParameterConstraintClause> constraintClauses,
            ArrayBuilder<ArrayBuilder<TypeConstraintSyntax>?> syntaxNodes,
            bool performOnlyCycleSafeValidation,
            BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(typeParameters.Length > 0);
            Debug.Assert(typeParameters.Length == constraintClauses.Count);
            int n = typeParameters.Length;
            for (int i = 0; i < n; i++)
            {
                constraintClauses[i] = RemoveInvalidConstraints(typeParameters[i], constraintClauses[i], syntaxNodes[i], performOnlyCycleSafeValidation, diagnostics);
            }
        }
 
        private static TypeParameterConstraintClause RemoveInvalidConstraints(
            TypeParameterSymbol typeParameter,
            TypeParameterConstraintClause constraintClause,
            ArrayBuilder<TypeConstraintSyntax>? syntaxNodesOpt,
            bool performOnlyCycleSafeValidation,
            BindingDiagnosticBag diagnostics)
        {
            if (syntaxNodesOpt != null)
            {
                var constraintTypes = constraintClause.ConstraintTypes;
                Symbol containingSymbol = typeParameter.ContainingSymbol;
 
                var constraintTypeBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance();
                int n = constraintTypes.Length;
 
                for (int i = 0; i < n; i++)
                {
                    var constraintType = constraintTypes[i];
                    var syntax = syntaxNodesOpt[i];
                    // Only valid constraint types are included in ConstraintTypes
                    // since, in general, it may be difficult to support all invalid types.
                    // In the future, we may want to include some invalid types
                    // though so the public binding API has the most information.
                    if (IsValidConstraint(typeParameter, syntax, constraintType, constraintClause.Constraints, constraintTypeBuilder, performOnlyCycleSafeValidation, diagnostics))
                    {
                        if (!performOnlyCycleSafeValidation)
                        {
                            CheckConstraintTypeVisibility(containingSymbol, syntax.Location, constraintType, diagnostics);
                        }
 
                        constraintTypeBuilder.Add(constraintType);
                    }
                }
 
                if (constraintTypeBuilder.Count < n)
                {
                    return TypeParameterConstraintClause.Create(constraintClause.Constraints, constraintTypeBuilder.ToImmutableAndFree());
                }
 
                constraintTypeBuilder.Free();
            }
 
            return constraintClause;
        }
 
        private static void CheckConstraintTypeVisibility(
            Symbol containingSymbol,
            Location location,
            TypeWithAnnotations constraintType,
            BindingDiagnosticBag diagnostics)
        {
            var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, containingSymbol.ContainingAssembly);
            if (!containingSymbol.IsNoMoreVisibleThan(constraintType, ref useSiteInfo))
            {
                // "Inconsistent accessibility: constraint type '{1}' is less accessible than '{0}'"
                diagnostics.Add(ErrorCode.ERR_BadVisBound, location, containingSymbol, constraintType.Type);
            }
 
            if (constraintType.Type.HasFileLocalTypes())
            {
                // if the containing symbol of the constraint is a member, we need to ensure the nearest containing type is a file-local type.
                var possibleFileType = containingSymbol switch
                {
                    TypeSymbol typeSymbol => typeSymbol,
                    LocalFunctionSymbol => null,
                    MethodSymbol method => (TypeSymbol)method.ContainingSymbol,
                    _ => throw ExceptionUtilities.UnexpectedValue(containingSymbol)
                };
                Debug.Assert(possibleFileType?.IsDefinition != false);
                if (possibleFileType?.HasFileLocalTypes() == false)
                {
                    diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, location, constraintType.Type, containingSymbol);
                }
            }
 
            diagnostics.Add(location, useSiteInfo);
        }
 
        /// <summary>
        /// Returns true if the constraint is valid. Otherwise
        /// returns false and generates a diagnostic.
        /// </summary>
        private static bool IsValidConstraint(
            TypeParameterSymbol typeParameter,
            TypeConstraintSyntax syntax,
            TypeWithAnnotations type,
            TypeParameterConstraintKind constraints,
            ArrayBuilder<TypeWithAnnotations> constraintTypes,
            bool performOnlyCycleSafeValidation,
            BindingDiagnosticBag diagnostics)
        {
            if (!isValidConstraintType(typeParameter, syntax, type, performOnlyCycleSafeValidation, diagnostics))
            {
                return false;
            }
 
            if (!performOnlyCycleSafeValidation && constraintTypes.Contains(c => type.Equals(c, TypeCompareKind.AllIgnoreOptions)))
            {
                // "Duplicate constraint '{0}' for type parameter '{1}'"
                Error(diagnostics, ErrorCode.ERR_DuplicateBound, syntax, type.Type.SetUnknownNullabilityForReferenceTypes(), typeParameter.Name);
                return false;
            }
 
            if (!type.DefaultType.IsTypeParameter() && // Doing an explicit check for type parameter on unresolved type to avoid cycles while calculating TypeKind. An unresolved type parameter cannot resolve to a class.   
                type.TypeKind == TypeKind.Class)
            {
                // If there is already a struct or class constraint (class constraint could be
                // 'class' or explicit type), report an error and drop this class. If we don't
                // drop this additional class, we may end up with conflicting class constraints.
 
                if (constraintTypes.Count > 0)
                {
                    // "The class type constraint '{0}' must come before any other constraints"
                    Error(diagnostics, ErrorCode.ERR_ClassBoundNotFirst, syntax, type.Type);
                    return false;
                }
 
                if ((constraints & (TypeParameterConstraintKind.ReferenceType)) != 0)
                {
                    switch (type.SpecialType)
                    {
                        case SpecialType.System_Enum:
                        case SpecialType.System_Delegate:
                        case SpecialType.System_MulticastDelegate:
                            break;
 
                        default:
                            // "'{0}': cannot specify both a constraint class and the 'class' or 'struct' constraint"
                            Error(diagnostics, ErrorCode.ERR_RefValBoundWithClass, syntax, type.Type);
                            return false;
                    }
                }
                else if (type.SpecialType != SpecialType.System_Enum)
                {
                    if ((constraints & TypeParameterConstraintKind.ValueType) != 0)
                    {
                        // "'{0}': cannot specify both a constraint class and the 'class' or 'struct' constraint"
                        Error(diagnostics, ErrorCode.ERR_RefValBoundWithClass, syntax, type.Type);
                        return false;
                    }
                    else if ((constraints & TypeParameterConstraintKind.Unmanaged) != 0)
                    {
                        // "'{0}': cannot specify both a constraint class and the 'unmanaged' constraint"
                        Error(diagnostics, ErrorCode.ERR_UnmanagedBoundWithClass, syntax, type.Type);
                        return false;
                    }
                }
            }
 
            return true;
 
            // Returns true if the type is a valid constraint type.
            // Otherwise returns false and generates a diagnostic.
            static bool isValidConstraintType(TypeParameterSymbol typeParameter, TypeConstraintSyntax syntax, TypeWithAnnotations typeWithAnnotations, bool performOnlyCycleSafeValidation, BindingDiagnosticBag diagnostics)
            {
                if (typeWithAnnotations.NullableAnnotation == NullableAnnotation.Annotated && performOnlyCycleSafeValidation &&
                    typeWithAnnotations.DefaultType is TypeParameterSymbol typeParameterInConstraint && typeParameterInConstraint.ContainingSymbol == (object)typeParameter.ContainingSymbol)
                {
                    return true;
                }
 
                TypeSymbol type = typeWithAnnotations.Type;
 
                switch (type.SpecialType)
                {
                    case SpecialType.System_Enum:
                        CheckFeatureAvailability(syntax, MessageID.IDS_FeatureEnumGenericTypeConstraint, diagnostics);
                        break;
 
                    case SpecialType.System_Delegate:
                    case SpecialType.System_MulticastDelegate:
                        CheckFeatureAvailability(syntax, MessageID.IDS_FeatureDelegateGenericTypeConstraint, diagnostics);
                        break;
 
                    case SpecialType.System_Object:
                    case SpecialType.System_ValueType:
                    case SpecialType.System_Array:
                        // "Constraint cannot be special class '{0}'"
                        Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, type);
                        return false;
                }
 
                switch (type.TypeKind)
                {
                    case TypeKind.Error:
                    case TypeKind.TypeParameter:
                        return true;
 
                    case TypeKind.Interface:
                        break;
 
                    case TypeKind.Dynamic:
                        // "Constraint cannot be the dynamic type"
                        Error(diagnostics, ErrorCode.ERR_DynamicTypeAsBound, syntax);
                        return false;
 
                    case TypeKind.Class:
                        if (type.IsSealed)
                        {
                            goto case TypeKind.Struct;
                        }
                        else if (type.IsStatic)
                        {
                            // "'{0}': static classes cannot be used as constraints"
                            Error(diagnostics, ErrorCode.ERR_ConstraintIsStaticClass, syntax, type);
                            return false;
                        }
                        break;
 
                    case TypeKind.Delegate:
                    case TypeKind.Enum:
                    case TypeKind.Struct:
                        // "'{0}' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter."
                        Error(diagnostics, ErrorCode.ERR_BadBoundType, syntax, type);
                        return false;
 
                    case TypeKind.Array:
                    case TypeKind.Pointer:
                    case TypeKind.FunctionPointer:
                        // "Invalid constraint type. A type used as a constraint must be an interface, a non-sealed class or a type parameter."
                        Error(diagnostics, ErrorCode.ERR_BadConstraintType, syntax.GetLocation());
                        return false;
 
                    case TypeKind.Submission:
                    // script class is synthesized, never used as a constraint
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(type.TypeKind);
                }
 
                if (type.ContainsDynamic())
                {
                    // "Constraint cannot be a dynamic type '{0}'"
                    Error(diagnostics, ErrorCode.ERR_ConstructedDynamicTypeAsBound, syntax, type);
                    return false;
                }
 
                return true;
            }
        }
    }
}