/// <summary> /// Bind and return a single type parameter constraint clause. /// </summary> private TypeParameterConstraintClause BindTypeParameterConstraints(TypeParameterSyntax typeParameterSyntax, TypeParameterConstraintClauseSyntax constraintClauseSyntax, DiagnosticBag diagnostics) { var constraints = TypeParameterConstraintKind.None; var constraintTypes = ArrayBuilder <TypeWithAnnotations> .GetInstance(); var syntaxBuilder = ArrayBuilder <TypeConstraintSyntax> .GetInstance(); SeparatedSyntaxList <TypeParameterConstraintSyntax> constraintsSyntax = constraintClauseSyntax.Constraints; Debug.Assert(!InExecutableBinder); // Cannot eagerly report diagnostics handled by LazyMissingNonNullTypesContextDiagnosticInfo bool hasTypeLikeConstraint = 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) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); } var constraintSyntax = (ClassOrStructConstraintSyntax)syntax; SyntaxToken questionToken = constraintSyntax.QuestionToken; if (questionToken.IsKind(SyntaxKind.QuestionToken)) { constraints |= TypeParameterConstraintKind.NullableReferenceType; LazyMissingNonNullTypesContextDiagnosticInfo.ReportNullableReferenceTypesIfNeeded(IsNullableEnabled(questionToken), questionToken.GetLocation(), diagnostics); } else if (IsNullableEnabled(constraintSyntax.ClassOrStructKeyword)) { constraints |= TypeParameterConstraintKind.NotNullableReferenceType; } else { constraints |= TypeParameterConstraintKind.ReferenceType; } continue; case SyntaxKind.StructConstraint: hasTypeLikeConstraint = true; if (i != 0) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.ValueType; continue; case SyntaxKind.ConstructorConstraint: 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) { diagnostics.Add(ErrorCode.ERR_NewBoundMustBeLast, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.Constructor; continue; case SyntaxKind.TypeConstraint: { hasTypeLikeConstraint = true; var typeConstraintSyntax = (TypeConstraintSyntax)syntax; var typeSyntax = typeConstraintSyntax.Type; var typeSyntaxKind = typeSyntax.Kind(); // For pointer types, don't report this error. It is already reported during binding typeSyntax below. switch (typeSyntaxKind) { case SyntaxKind.PredefinedType: case SyntaxKind.PointerType: case SyntaxKind.NullableType: break; default: if (!SyntaxFacts.IsName(typeSyntax.Kind())) { diagnostics.Add(ErrorCode.ERR_BadConstraintType, typeSyntax.GetLocation()); } break; } var type = BindTypeOrUnmanagedKeyword(typeSyntax, diagnostics, out var isUnmanaged); if (isUnmanaged) { if (constraints != 0 || constraintTypes.Any()) { diagnostics.Add(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, typeSyntax.GetLocation()); 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; } constraintTypes.Add(type); syntaxBuilder.Add(typeConstraintSyntax); } continue; default: throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); } } if (!hasTypeLikeConstraint && !IsNullableEnabled(typeParameterSyntax.Identifier)) { constraints |= TypeParameterConstraintKind.ObliviousNullabilityIfReferenceType; } return(TypeParameterConstraintClause.Create(constraints, constraintTypes.ToImmutableAndFree(), syntaxBuilder.ToImmutableAndFree())); }
/// <summary> /// Bind and return a single type parameter constraint clause. /// </summary> private TypeParameterConstraintClause BindTypeParameterConstraints( string name, SeparatedSyntaxList <TypeParameterConstraintSyntax> constraintsSyntax, DiagnosticBag diagnostics) { var constraints = TypeParameterConstraintKind.None; var constraintTypes = ArrayBuilder <TypeSymbol> .GetInstance(); for (int i = 0, n = constraintsSyntax.Count; i < n; i++) { var syntax = constraintsSyntax[i]; switch (syntax.Kind()) { case SyntaxKind.ClassConstraint: if (i != 0) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.ReferenceType; continue; case SyntaxKind.StructConstraint: if (i != 0) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.ValueType; continue; case SyntaxKind.ConstructorConstraint: 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) { diagnostics.Add(ErrorCode.ERR_NewBoundMustBeLast, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.Constructor; continue; case SyntaxKind.TypeConstraint: { var typeConstraintSyntax = (TypeConstraintSyntax)syntax; var typeSyntax = typeConstraintSyntax.Type; var typeSyntaxKind = typeSyntax.Kind(); // For pointer types, don't report this error. It is already reported during binding typeSyntax below. if (typeSyntaxKind != SyntaxKind.PredefinedType && typeSyntaxKind != SyntaxKind.PointerType && !SyntaxFacts.IsName(typeSyntax.Kind())) { diagnostics.Add(ErrorCode.ERR_BadConstraintType, typeSyntax.GetLocation()); } var type = BindTypeOrUnmanagedKeyword(typeSyntax, diagnostics, out var isUnmanaged); if (isUnmanaged) { if (constraints != 0 || constraintTypes.Any()) { diagnostics.Add(ErrorCode.ERR_UnmanagedConstraintMustBeFirst, typeSyntax.GetLocation()); 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; } else { // 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 (!IsValidConstraintType(typeConstraintSyntax, type, diagnostics)) { continue; } if (constraintTypes.Contains(type)) { // "Duplicate constraint '{0}' for type parameter '{1}'" Error(diagnostics, ErrorCode.ERR_DuplicateBound, syntax, type, name); continue; } if (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); continue; } 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); continue; } } 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); continue; } else if ((constraints & TypeParameterConstraintKind.Unmanaged) != 0) { // "'{0}': cannot specify both a constraint class and the 'unmanaged' constraint" Error(diagnostics, ErrorCode.ERR_UnmanagedBoundWithClass, syntax, type); continue; } } } } constraintTypes.Add(type); } continue; default: throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); } } return(new TypeParameterConstraintClause(constraints, constraintTypes.ToImmutableAndFree())); }
/// <summary> /// Bind and return a single type parameter constraint clause. /// </summary> private TypeParameterConstraintClause BindTypeParameterConstraints( string name, SeparatedSyntaxList <TypeParameterConstraintSyntax> constraintsSyntax, DiagnosticBag diagnostics) { var constraints = TypeParameterConstraintKind.None; var constraintTypes = ArrayBuilder <TypeSymbol> .GetInstance(); var isStruct = false; for (int i = 0, n = constraintsSyntax.Count; i < n; i++) { var syntax = constraintsSyntax[i]; switch (syntax.Kind()) { case SyntaxKind.ClassConstraint: if (i != 0) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.ReferenceType; continue; case SyntaxKind.StructConstraint: if (i != 0) { diagnostics.Add(ErrorCode.ERR_RefValBoundMustBeFirst, syntax.GetFirstToken().GetLocation()); } isStruct = true; constraints |= TypeParameterConstraintKind.ValueType; continue; case SyntaxKind.ConstructorConstraint: if (isStruct) { diagnostics.Add(ErrorCode.ERR_NewBoundWithVal, syntax.GetFirstToken().GetLocation()); } if (i != n - 1) { diagnostics.Add(ErrorCode.ERR_NewBoundMustBeLast, syntax.GetFirstToken().GetLocation()); } constraints |= TypeParameterConstraintKind.Constructor; continue; case SyntaxKind.TypeConstraint: { var typeConstraintSyntax = (TypeConstraintSyntax)syntax; var typeSyntax = typeConstraintSyntax.Type; if (typeSyntax.Kind() != SyntaxKind.PredefinedType && !SyntaxFacts.IsName(typeSyntax.Kind())) { diagnostics.Add(ErrorCode.ERR_BadConstraintType, typeSyntax.GetLocation()); } var type = this.BindType(typeSyntax, diagnostics); // 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 (!IsValidConstraintType(typeConstraintSyntax, type, diagnostics)) { continue; } if (constraintTypes.Contains(type)) { // "Duplicate constraint '{0}' for type parameter '{1}'" Error(diagnostics, ErrorCode.ERR_DuplicateBound, syntax, type, name); continue; } if (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); continue; } if ((constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) != 0) { // "'{0}': cannot specify both a constraint class and the 'class' or 'struct' constraint" Error(diagnostics, ErrorCode.ERR_RefValBoundWithClass, syntax, type); continue; } } constraintTypes.Add(type); } continue; default: throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); } } return(new TypeParameterConstraintClause(constraints, constraintTypes.ToImmutableAndFree())); }