|
' 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.
Imports System.Collections.Immutable
Imports System.Diagnostics
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
''' <summary>
''' A tuple of TypeParameterSymbol and DiagnosticInfo, created for errors
''' reported from ConstraintsHelper rather than creating Diagnostics directly.
''' This decouples constraints checking from syntax and Locations, and supports
''' callers that may want to create Location instances lazily or not at all.
''' </summary>
Friend Structure TypeParameterDiagnosticInfo
Public Sub New(typeParameter As TypeParameterSymbol, useSiteInfo As UseSiteInfo(Of AssemblySymbol))
Me.TypeParameter = typeParameter
Me.UseSiteInfo = useSiteInfo
End Sub
Public Sub New(typeParameter As TypeParameterSymbol, diagnostic As DiagnosticInfo)
Me.New(typeParameter, New UseSiteInfo(Of AssemblySymbol)(diagnostic))
End Sub
Public Sub New(typeParameter As TypeParameterSymbol, constraint As TypeParameterConstraint, diagnostic As DiagnosticInfo)
Me.New(typeParameter, diagnostic)
Me.Constraint = constraint
End Sub
Public ReadOnly TypeParameter As TypeParameterSymbol
Public ReadOnly Constraint As TypeParameterConstraint
Public ReadOnly UseSiteInfo As UseSiteInfo(Of AssemblySymbol)
End Structure
<Flags()>
Friend Enum DirectConstraintConflictKind
None = 0
DuplicateTypeConstraint = 1 << 0
RedundantConstraint = 1 << 1
All = (1 << 2) - 1
End Enum
''' <summary>
''' Helper methods for generic type parameter constraints. There are two sets of methods: one
''' set for resolving constraint "bounds" (that is, determining the effective base type, interface set,
''' etc.), and another set for checking for constraint violations in type and method references.
'''
''' Bounds are resolved by calling one of the ResolveBounds overloads. Typically bounds are
''' resolved by each TypeParameterSymbol at, or before, one of the corresponding properties
''' (BaseType, Interfaces, etc.) is accessed. Resolving bounds may result in errors (cycles,
''' inconsistent constraints, etc.) and it is the responsibility of the caller to report any such
''' errors as declaration errors or use-site errors (depending on whether the type parameter
''' was from source or metadata) and to ensure bounds are resolved for source type parameters
''' even if the corresponding properties are never accessed directly.
'''
''' Constraints are checked by calling one of the CheckConstraints or CheckAllConstraints
''' overloads for any generic type or method reference from source. In some circumstances,
''' references are checked at the time the generic type or generic method is bound and constructed
''' by the Binder. In those case, it is sufficient to call one of the CheckConstraints overloads
''' since compound types (such as A(Of T).B(Of U) or A(Of B(Of T))) are checked incrementally
''' as each part is bound. In other cases however, constraint checking needs to be delayed to
''' prevent cycles where checking constraints requires binding the syntax that is currently
''' being bound (such as the constraint in Class C(Of T As C(Of T)). In those cases, the caller
''' must lazily check constraints, and since the types may be compound types, it is necessary
''' to call CheckAllConstraints.
''' </summary>
Friend Module ConstraintsHelper
''' <summary>
''' Enum used internally by RemoveDirectConstraintConflicts to
''' track what type constraint has been seen, to report conflicts
''' between { 'Structure', 'Class', [explicit type] }. The 'New'
''' constraint does not need to be tracked for those conflicts.
''' </summary>
Private Enum DirectTypeConstraintKind
None
ReferenceTypeConstraint
ValueTypeConstraint
ExplicitType
End Enum
''' <summary>
''' Return the constraints for the type parameter with any cycles
''' or conflicting constraints reported as errors and removed.
''' </summary>
<Extension()>
Public Function RemoveDirectConstraintConflicts(
typeParameter As TypeParameterSymbol,
constraints As ImmutableArray(Of TypeParameterConstraint),
inProgress As ConsList(Of TypeParameterSymbol),
reportConflicts As DirectConstraintConflictKind,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo)) As ImmutableArray(Of TypeParameterConstraint)
If constraints.Length > 0 Then
Dim constraintsBuilder = ArrayBuilder(Of TypeParameterConstraint).GetInstance()
Dim containingSymbol = typeParameter.ContainingSymbol
Dim explicitKind = DirectTypeConstraintKind.None
Dim reportRedundantConstraints = (reportConflicts And DirectConstraintConflictKind.RedundantConstraint) <> 0
For Each constraint In constraints
' See Bindable::ValidateDirectConstraint.
Select Case constraint.Kind
Case TypeParameterConstraintKind.ReferenceType
If reportRedundantConstraints Then
If explicitKind = DirectTypeConstraintKind.None Then
explicitKind = DirectTypeConstraintKind.ReferenceTypeConstraint
Else
' Combinations of {Class, Structure} should have
' been caught and discarded during binding.
Debug.Assert(explicitKind = DirectTypeConstraintKind.ExplicitType)
' "'Class' constraint and a specific class type constraint cannot be combined."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_RefAndClassTypeConstrCombined)))
Continue For
End If
End If
Case TypeParameterConstraintKind.ValueType
If reportRedundantConstraints Then
If explicitKind = DirectTypeConstraintKind.None Then
explicitKind = DirectTypeConstraintKind.ValueTypeConstraint
Else
' Combinations of {Class, Structure} should have
' been caught and discarded during binding.
Debug.Assert(explicitKind = DirectTypeConstraintKind.ExplicitType)
' "'Structure' constraint and a specific class type constraint cannot be combined."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_ValueAndClassTypeConstrCombined)))
Continue For
End If
End If
Case TypeParameterConstraintKind.None
Dim constraintType = constraint.TypeConstraint
Dim duplicate As Boolean = ContainsTypeConstraint(constraintsBuilder, constraintType)
' Check for duplicate type constraints.
If duplicate Then
If (reportConflicts And DirectConstraintConflictKind.DuplicateTypeConstraint) <> 0 Then
' "Constraint type '{0}' already specified for this type parameter.'
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_ConstraintAlreadyExists1, constraintType)))
End If
' Continue with other checks for this constraint type, even
' though it's a duplicate, for consistency with Dev10.
End If
Select Case constraintType.TypeKind
Case TypeKind.Class
If reportRedundantConstraints Then
Select Case explicitKind
Case DirectTypeConstraintKind.None
Dim classType = DirectCast(constraintType, NamedTypeSymbol)
If classType.IsNotInheritable Then
' "Type constraint cannot be a 'NotInheritable' class."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_ClassConstraintNotInheritable1)))
Else
Select Case constraintType.SpecialType
Case SpecialType.System_Object,
SpecialType.System_ValueType,
SpecialType.System_Enum,
SpecialType.System_Delegate,
SpecialType.System_MulticastDelegate,
SpecialType.System_Array
' "'{0}' cannot be used as a type constraint."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_ConstraintIsRestrictedType1, constraintType)))
End Select
End If
explicitKind = DirectTypeConstraintKind.ExplicitType
Case DirectTypeConstraintKind.ReferenceTypeConstraint
' "'Class' constraint and a specific class type constraint cannot be combined."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_RefAndClassTypeConstrCombined)))
Continue For
Case DirectTypeConstraintKind.ValueTypeConstraint
' "'Structure' constraint and a specific class type constraint cannot be combined."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_ValueAndClassTypeConstrCombined)))
Continue For
Case DirectTypeConstraintKind.ExplicitType
' "Type parameter '{0}' can only have one constraint that is a class."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_MultipleClassConstraints1, typeParameter)))
Continue For
End Select
End If
Case TypeKind.Interface,
TypeKind.Error
Case TypeKind.Module
' No error reported for Module. If the type reference was in source, BC30371
' ERR_ModuleAsType1 will have been reported binding the type reference.
Case TypeKind.TypeParameter
Dim constraintTypeParameter = DirectCast(constraintType, TypeParameterSymbol)
If constraintTypeParameter.ContainingSymbol = containingSymbol Then
' The constraint type parameter is from the same containing type or method.
If inProgress.ContainsReference(constraintTypeParameter) Then
' "Type parameter '{0}' cannot be constrained to itself: {1}"
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(constraintTypeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_ConstraintCycle2, constraintTypeParameter, GetConstraintCycleInfo(inProgress))))
Continue For
Else
' Traverse constraint type parameter constraints to detect cycles.
constraintTypeParameter.ResolveConstraints(inProgress)
End If
End If
If reportRedundantConstraints AndAlso constraintTypeParameter.HasValueTypeConstraint Then
' "Type parameter with a 'Structure' constraint cannot be used as a constraint."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(constraintTypeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_TypeParamWithStructConstAsConst)))
Continue For
End If
Case TypeKind.Array,
TypeKind.Delegate,
TypeKind.Enum,
TypeKind.Structure
If reportRedundantConstraints Then
' "Type constraint '{0}' must be either a class, interface or type parameter."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
constraint,
ErrorFactory.ErrorInfo(ERRID.ERR_ConstNotClassInterfaceOrTypeParam1, constraintType)))
End If
Case Else
Throw ExceptionUtilities.UnexpectedValue(constraintType.TypeKind)
End Select
If duplicate Then
Continue For
End If
End Select
constraintsBuilder.Add(constraint)
Next
If constraintsBuilder.Count <> constraints.Length Then
constraints = constraintsBuilder.ToImmutable()
End If
constraintsBuilder.Free()
End If
Return constraints
End Function
' Currently, this method should be called for SourceTypeParameterSymbols
' only, not PETypeParameterSymbols. If that changes, and this method is
' called for type parameters from metadata, we need to ensure a use-site
' error is generated if conflicts are found. Add a unit test if that's the case.
' See Bindable::ValidateIndirectConstraints.
<Extension()>
Public Sub ReportIndirectConstraintConflicts(
typeParameter As SourceTypeParameterSymbol,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo))
Dim constraints = ArrayBuilder(Of TypeParameterAndConstraint).GetInstance()
typeParameter.GetAllConstraints(constraints, fromConstraintOpt:=Nothing)
Dim n = constraints.Count
For i = 0 To n - 1
Dim pair1 = constraints(i)
If pair1.IsBad Then
Continue For
End If
For j = i + 1 To n - 1
Dim pair2 = constraints(j)
If pair2.IsBad Then
Continue For
End If
Dim bad = False
Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(typeParameter.ContainingAssembly)
If (pair1.TypeParameter Is typeParameter) AndAlso (pair2.TypeParameter Is typeParameter) Then
' Check direct constraints to handle inherited constraints on overridden methods.
If HasConflict(pair1.Constraint, pair2.Constraint, useSiteInfo) Then
' "Constraint '{0}' conflicts with the constraint '{1}' already specified for type parameter '{2}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
pair2.Constraint,
ErrorFactory.ErrorInfo(
ERRID.ERR_ConflictingDirectConstraints3,
pair2.Constraint.ToDisplayFormat(),
pair1.Constraint.ToDisplayFormat(),
typeParameter)))
bad = True
End If
ElseIf (pair1.TypeParameter Is pair2.TypeParameter) Then
' Skip other cases where both constraints are from the same type
' parameter but not the current type parameter since those cases
' will be reported directly on the other type parameter.
ElseIf HasConflict(pair1.Constraint, pair2.Constraint, useSiteInfo) Then
If pair1.TypeParameter Is typeParameter Then
' "Constraint '{0}' conflicts with the indirect constraint '{1}' obtained from the type parameter constraint '{2}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
pair1.Constraint,
ErrorFactory.ErrorInfo(
ERRID.ERR_ConstraintClashDirectIndirect3,
pair1.Constraint.ToDisplayFormat(),
pair2.Constraint.ToDisplayFormat(),
pair2.TypeParameter)))
bad = True
ElseIf pair2.TypeParameter Is typeParameter Then
' "Indirect constraint '{0}' obtained from the type parameter constraint '{1}' conflicts with the constraint '{2}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
pair1.Constraint,
ErrorFactory.ErrorInfo(
ERRID.ERR_ConstraintClashIndirectDirect3,
pair1.Constraint.ToDisplayFormat(),
pair1.TypeParameter,
pair2.Constraint.ToDisplayFormat())))
bad = True
Else
' "Indirect constraint '{0}' obtained from the type parameter constraint '{1}' conflicts with the indirect constraint '{2}' obtained from the type parameter constraint '{3}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
pair2.Constraint,
ErrorFactory.ErrorInfo(
ERRID.ERR_ConstraintClashIndirectIndirect4,
pair2.Constraint.ToDisplayFormat(),
pair2.TypeParameter,
pair1.Constraint.ToDisplayFormat(),
pair1.TypeParameter)))
bad = True
End If
End If
If AppendUseSiteInfo(useSiteInfo, typeParameter, useSiteDiagnosticsBuilder) Then
bad = True
End If
If bad Then
constraints(j) = pair2.ToBad()
End If
Next
Next
constraints.Free()
End Sub
<Extension()>
Public Sub CheckAllConstraints(
type As TypeSymbol,
languageVersion As LanguageVersion,
loc As Location,
diagnostics As BindingDiagnosticBag,
template As CompoundUseSiteInfo(Of AssemblySymbol))
Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance()
Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing
type.CheckAllConstraints(languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
If useSiteDiagnosticsBuilder IsNot Nothing Then
diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder)
End If
For Each diagnostic In diagnosticsBuilder
diagnostics.Add(diagnostic.UseSiteInfo, loc)
Next
diagnosticsBuilder.Free()
End Sub
''' <summary>
''' Check all generic constraints on the given type and any containing types
''' (such as A(Of T) in A(Of T).B(Of U)). This includes checking constraints
''' on generic types within the type (such as B(Of T) in A(Of B(Of T)())).
''' </summary>
<Extension()>
Public Sub CheckAllConstraints(
type As TypeSymbol,
languageVersion As LanguageVersion,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
template As CompoundUseSiteInfo(Of AssemblySymbol))
Dim diagnostics As New CheckConstraintsDiagnosticsBuilders()
diagnostics.diagnosticsBuilder = diagnosticsBuilder
diagnostics.useSiteDiagnosticsBuilder = useSiteDiagnosticsBuilder
diagnostics.template = template
diagnostics.languageVersion = languageVersion
type.VisitType(s_checkConstraintsSingleTypeFunc, diagnostics)
useSiteDiagnosticsBuilder = diagnostics.useSiteDiagnosticsBuilder
End Sub
Private Class CheckConstraintsDiagnosticsBuilders
Public diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo)
Public useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo)
Public template As CompoundUseSiteInfo(Of AssemblySymbol)
Public languageVersion As LanguageVersion
End Class
Private ReadOnly s_checkConstraintsSingleTypeFunc As Func(Of TypeSymbol, CheckConstraintsDiagnosticsBuilders, Boolean) = AddressOf CheckConstraintsSingleType
Private Function CheckConstraintsSingleType(type As TypeSymbol, diagnostics As CheckConstraintsDiagnosticsBuilders) As Boolean
If type.Kind = SymbolKind.NamedType Then
DirectCast(type, NamedTypeSymbol).CheckConstraints(
diagnostics.languageVersion, diagnostics.diagnosticsBuilder, diagnostics.useSiteDiagnosticsBuilder, diagnostics.template)
End If
Return False ' continue walking types
End Function
<Extension()>
Public Sub CheckConstraints(
tuple As TupleTypeSymbol,
syntaxNode As SyntaxNode,
elementLocations As ImmutableArray(Of Location),
diagnostics As BindingDiagnosticBag,
template As CompoundUseSiteInfo(Of AssemblySymbol))
Dim type As NamedTypeSymbol = tuple.TupleUnderlyingType
If Not RequiresChecking(type) Then
Return
End If
If syntaxNode.HasErrors Then
Return
End If
Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance()
Dim underlyingTupleTypeChain = ArrayBuilder(Of NamedTypeSymbol).GetInstance
TupleTypeSymbol.GetUnderlyingTypeChain(type, underlyingTupleTypeChain)
Dim offset As Integer = 0
For Each underlyingTuple In underlyingTupleTypeChain
Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing
CheckTypeConstraints(underlyingTuple,
DirectCast(syntaxNode.SyntaxTree.Options, VisualBasicParseOptions).LanguageVersion,
diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
If useSiteDiagnosticsBuilder IsNot Nothing Then
diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder)
End If
For Each diagnostic In diagnosticsBuilder
Dim ordinal = diagnostic.TypeParameter.Ordinal
' If this is the TRest type parameter, we report it on
' the entire type syntax as it does not map to any tuple element.
Dim location = If(ordinal = TupleTypeSymbol.RestIndex, syntaxNode.Location, elementLocations(ordinal + offset))
diagnostics.Add(diagnostic.UseSiteInfo, location)
Next
diagnosticsBuilder.Clear()
offset += TupleTypeSymbol.RestIndex
Next
underlyingTupleTypeChain.Free()
diagnosticsBuilder.Free()
End Sub
<Extension()>
Public Function CheckConstraintsForNonTuple(
type As NamedTypeSymbol,
languageVersion As LanguageVersion,
typeArgumentsSyntax As SeparatedSyntaxList(Of TypeSyntax),
diagnostics As BindingDiagnosticBag,
template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Debug.Assert(Not type.IsTupleType)
Debug.Assert(typeArgumentsSyntax.Count = type.Arity)
If Not RequiresChecking(type) Then
Return True
End If
Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance()
Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing
Dim result = CheckTypeConstraints(type, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
If useSiteDiagnosticsBuilder IsNot Nothing Then
diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder)
End If
For Each diagnostic In diagnosticsBuilder
Dim ordinal = diagnostic.TypeParameter.Ordinal
Dim location = typeArgumentsSyntax(ordinal).GetLocation()
diagnostics.Add(diagnostic.UseSiteInfo, location)
Next
diagnosticsBuilder.Free()
Return result
End Function
<Extension()>
Public Function CheckConstraints(
type As NamedTypeSymbol,
languageVersion As LanguageVersion,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
' We do not report element locations in method parameters and return types
' so we will simply unwrap the type if it was a tuple. We are relying on
' TypeSymbolExtensions.VisitType to dig into the "Rest" tuple so that they
' will be recursively unwrapped as well.
type = DirectCast(type.GetTupleUnderlyingTypeOrSelf(), NamedTypeSymbol)
If Not RequiresChecking(type) Then
Return True
End If
Return CheckTypeConstraints(type, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
End Function
<Extension()>
Public Function CheckConstraints(
method As MethodSymbol,
languageVersion As LanguageVersion,
diagnosticLocation As Location,
diagnostics As BindingDiagnosticBag,
template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
If Not RequiresChecking(method) Then
Return True
End If
Dim diagnosticsBuilder = ArrayBuilder(Of TypeParameterDiagnosticInfo).GetInstance()
Dim useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo) = Nothing
Dim result = CheckMethodConstraints(method, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
If useSiteDiagnosticsBuilder IsNot Nothing Then
diagnosticsBuilder.AddRange(useSiteDiagnosticsBuilder)
End If
For Each diagnostic In diagnosticsBuilder
diagnostics.Add(diagnostic.UseSiteInfo, diagnosticLocation)
Next
diagnosticsBuilder.Free()
Return result
End Function
<Extension()>
Public Function CheckConstraints(
method As MethodSymbol,
languageVersion As LanguageVersion,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
If Not RequiresChecking(method) Then
Return True
End If
Return CheckMethodConstraints(method, languageVersion, diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
End Function
Private Function CheckTypeConstraints(
type As NamedTypeSymbol,
languageVersion As LanguageVersion,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Dim substitution = type.TypeSubstitution
Return CheckConstraints(type, languageVersion, substitution, type.OriginalDefinition.TypeParameters, type.TypeArgumentsNoUseSiteDiagnostics, diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
End Function
Private Function CheckMethodConstraints(
method As MethodSymbol,
languageVersion As LanguageVersion,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Dim substitution = DirectCast(method, SubstitutedMethodSymbol).TypeSubstitution
Return CheckConstraints(method, languageVersion, substitution, method.OriginalDefinition.TypeParameters, method.TypeArguments, diagnosticsBuilder, useSiteDiagnosticsBuilder, template)
End Function
''' <summary>
''' Check type parameters for the containing type or method symbol.
''' The type parameters are assumed to be the original definitions of type
''' parameters from the containing type or method, and the TypeSubstitution
''' instance is used for substituting type parameters within the constraints
''' of those type parameters, so the substitution should map from type
''' parameters to type arguments.
''' </summary>
<Extension()>
Public Function CheckConstraints(
constructedSymbol As Symbol,
languageVersion As LanguageVersion,
substitution As TypeSubstitution,
typeParameters As ImmutableArray(Of TypeParameterSymbol),
typeArguments As ImmutableArray(Of TypeSymbol),
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
template As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Debug.Assert(typeParameters.Length = typeArguments.Length)
Dim n = typeParameters.Length
Dim succeeded = True
For i = 0 To n - 1
Dim typeArgument = typeArguments(i)
Dim typeParameter = typeParameters(i)
Dim useSiteInfo As New CompoundUseSiteInfo(Of AssemblySymbol)(template)
If Not CheckConstraints(constructedSymbol, languageVersion, substitution, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then
succeeded = False
End If
If AppendUseSiteInfo(useSiteInfo, typeParameter, useSiteDiagnosticsBuilder) Then
succeeded = False
End If
Next
Return succeeded
End Function
Public Function CheckConstraints(
constructedSymbol As Symbol,
languageVersion As LanguageVersion,
substitution As TypeSubstitution,
typeParameter As TypeParameterSymbol,
typeArgument As TypeSymbol,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
' The type parameters must be original definitions of type parameters from the containing symbol.
Debug.Assert(((constructedSymbol Is Nothing) AndAlso (substitution Is Nothing)) OrElse
(typeParameter.ContainingSymbol Is constructedSymbol.OriginalDefinition))
If typeArgument.IsErrorType() Then
Return True
End If
Dim succeeded = True
If typeArgument.IsRestrictedType() Then
If diagnosticsBuilder IsNot Nothing Then
' "'{0}' cannot be made nullable, and cannot be used as the data type of an array element, field, anonymous type member, type argument, 'ByRef' parameter, or return statement."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_RestrictedType1, typeArgument)))
End If
succeeded = False
End If
If typeParameter.HasConstructorConstraint AndAlso Not SatisfiesConstructorConstraint(typeParameter, typeArgument, diagnosticsBuilder) Then
succeeded = False
End If
If typeParameter.HasReferenceTypeConstraint AndAlso Not SatisfiesReferenceTypeConstraint(typeParameter, typeArgument, diagnosticsBuilder) Then
succeeded = False
End If
If typeParameter.HasUnmanagedTypeConstraint Then
If Not InternalSyntax.Parser.CheckFeatureAvailability(languageVersion, InternalSyntax.Feature.UnmanagedConstraint) Then
useSiteInfo.AddDiagnosticInfo(InternalSyntax.Parser.GetFeatureAvailabilityError(InternalSyntax.Feature.UnmanagedConstraint, languageVersion))
succeeded = False
End If
Dim managedKind = typeArgument.GetManagedKind(useSiteInfo)
If managedKind = ManagedKind.Managed Then
If diagnosticsBuilder IsNot Nothing Then
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_UnmanagedConstraintNotSatisfied, typeArgument, typeParameter)))
End If
succeeded = False
End If
End If
If typeParameter.HasValueTypeConstraint AndAlso Not SatisfiesValueTypeConstraint(constructedSymbol, typeParameter, typeArgument, diagnosticsBuilder, useSiteInfo) Then
succeeded = False
End If
' The type parameters for a constructed type/method are the type parameters of the ConstructedFrom
' type/method, so the constraint types are not substituted. For instance with "Class C(Of T As U, U)",
' the type parameter for T in "C(Of Object, Integer)" has constraint "U", not "Integer". We need to
' substitute the type parameter constraints from the original definition of the type parameters
' using the TypeSubstitution from the constructed type/method.
For Each t In typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(useSiteInfo)
Dim constraintType = t.InternalSubstituteTypeParameters(substitution).Type
If Not SatisfiesTypeConstraint(typeArgument, constraintType, useSiteInfo) Then
If diagnosticsBuilder IsNot Nothing Then
' "Type argument '{0}' does not inherit from or implement the constraint type '{1}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_GenericConstraintNotSatisfied2, typeArgument, constraintType)))
End If
succeeded = False
End If
Next
Return succeeded
End Function
Private Function AppendUseSiteInfo(
useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
typeParameter As TypeParameterSymbol,
<[In], Out> ByRef useSiteDiagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo)
) As Boolean
Dim hasErrors As Boolean = useSiteInfo.AccumulatesDiagnostics AndAlso Not useSiteInfo.Diagnostics.IsNullOrEmpty
If Not hasErrors AndAlso (Not useSiteInfo.AccumulatesDependencies OrElse useSiteInfo.Dependencies.IsNullOrEmpty) Then
Return False
End If
If useSiteDiagnosticsBuilder Is Nothing Then
useSiteDiagnosticsBuilder = New ArrayBuilder(Of TypeParameterDiagnosticInfo)()
End If
If Not hasErrors Then
If useSiteInfo.AccumulatesDependencies AndAlso Not useSiteInfo.Dependencies.IsNullOrEmpty() Then
useSiteDiagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter,
If(useSiteInfo.Dependencies.Count = 1,
New UseSiteInfo(Of AssemblySymbol)(useSiteInfo.Dependencies.Single()),
New UseSiteInfo(Of AssemblySymbol)(useSiteInfo.Dependencies.ToImmutableHashSet()))))
End If
Return False
End If
For Each info In useSiteInfo.Diagnostics
Debug.Assert(info.Severity = DiagnosticSeverity.Error)
useSiteDiagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, info))
Next
Return True
End Function
''' <summary>
''' Return the most derived type from the set of constraint types on this type
''' parameter and any type parameter it depends on. Returns Nothing if there
''' are no concrete constraint types. If there are multiple constraints, returns
''' the most derived, ignoring any subsequent constraints that are neither
''' more or less derived. This method assumes there are no constraint cycles.
''' </summary>
<Extension()>
Public Function GetNonInterfaceConstraint(typeParameter As TypeParameterSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As TypeSymbol
Dim result As TypeSymbol = Nothing
For Each constraint In typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(useSiteInfo)
Dim candidate As TypeSymbol = Nothing
Select Case constraint.Kind
Case SymbolKind.ErrorType
Continue For
Case SymbolKind.TypeParameter
candidate = DirectCast(constraint, TypeParameterSymbol).GetNonInterfaceConstraint(useSiteInfo)
Case Else
If Not constraint.IsInterfaceType() Then
candidate = constraint
End If
End Select
If result Is Nothing Then
result = candidate
ElseIf candidate IsNot Nothing Then
' Pick the most derived type
If result.IsClassType AndAlso Conversions.IsDerivedFrom(candidate, result, useSiteInfo) Then
result = candidate
End If
End If
Next
Return result
End Function
<Extension()>
Public Function HasInterfaceConstraint(typeParameter As TypeParameterSymbol) As Boolean
Dim result As TypeSymbol = Nothing
For Each constraint In typeParameter.ConstraintTypesNoUseSiteDiagnostics
Select Case constraint.Kind
Case SymbolKind.ErrorType
Continue For
Case SymbolKind.TypeParameter
If DirectCast(constraint, TypeParameterSymbol).HasInterfaceConstraint() Then
Return True
End If
Case Else
If constraint.IsInterfaceType() Then
Return True
End If
End Select
Next
Return False
End Function
''' <summary>
''' Return the most derived class type from the set of constraint types on this type
''' parameter and any type parameter it depends on. Returns Nothing if there are
''' no concrete constraint types. If there are multiple constraints, returns the most
''' derived, ignoring any subsequent constraints that are neither more or less derived.
''' This method assumes there are no constraint cycles. Unlike GetBaseConstraintType,
''' this method will always return a NamedTypeSymbol representing a class: returning
''' System.ValueType for value types, System.Array for arrays, and System.Enum for enums.
''' </summary>
<Extension()>
Public Function GetClassConstraint(typeParameter As TypeParameterSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As NamedTypeSymbol
Dim baseType = typeParameter.GetNonInterfaceConstraint(useSiteInfo)
If baseType Is Nothing Then
Return Nothing
End If
Select Case baseType.TypeKind
Case TypeKind.Array,
TypeKind.Enum,
TypeKind.Structure
Return baseType.BaseTypeWithDefinitionUseSiteDiagnostics(useSiteInfo)
Case Else
Debug.Assert(Not baseType.IsInterfaceType())
Debug.Assert(baseType.TypeKind <> TypeKind.TypeParameter)
Return DirectCast(baseType, NamedTypeSymbol)
End Select
End Function
''' <summary>
''' Populate the collection with all constraints for the type parameter, traversing
''' any constraints that are also type parameters. The result is a collection of type
''' and flag constraints, with no type parameter references. This method assumes
''' there are no constraint cycles.
''' </summary>
<Extension()>
Private Sub GetAllConstraints(
typeParameter As TypeParameterSymbol,
constraintsBuilder As ArrayBuilder(Of TypeParameterAndConstraint),
fromConstraintOpt As TypeParameterConstraint?)
Dim constraints = ArrayBuilder(Of TypeParameterConstraint).GetInstance()
typeParameter.GetConstraints(constraints)
For Each constraint In constraints
Dim type = constraint.TypeConstraint
If type IsNot Nothing Then
Select Case type.TypeKind
Case TypeKind.TypeParameter
' Add constraints from type parameter.
DirectCast(type, TypeParameterSymbol).GetAllConstraints(constraintsBuilder, If(fromConstraintOpt.HasValue, fromConstraintOpt.Value, constraint))
Continue For
Case TypeKind.Error
' Skip error types.
Continue For
End Select
End If
constraintsBuilder.Add(
If(fromConstraintOpt.HasValue,
New TypeParameterAndConstraint(DirectCast(fromConstraintOpt.Value.TypeConstraint, TypeParameterSymbol), constraint.AtLocation(fromConstraintOpt.Value.LocationOpt)),
New TypeParameterAndConstraint(typeParameter, constraint)))
Next
constraints.Free()
End Sub
''' <summary>
''' A tuple of type parameter and constraint type.
''' </summary>
Private Structure TypeParameterAndConstraint
Public Sub New(typeParameter As TypeParameterSymbol, constraint As TypeParameterConstraint, Optional isBad As Boolean = False)
Me.TypeParameter = typeParameter
Me.Constraint = constraint
Me.IsBad = isBad
End Sub
Public ReadOnly TypeParameter As TypeParameterSymbol
Public ReadOnly Constraint As TypeParameterConstraint
Public ReadOnly IsBad As Boolean
Public Function ToBad() As TypeParameterAndConstraint
Debug.Assert(Not IsBad)
Return New TypeParameterAndConstraint(TypeParameter, Constraint, True)
End Function
Public Overrides Function ToString() As String
Dim result = String.Format("{0} : {1}", TypeParameter, Constraint)
If IsBad Then
result = result & " (bad)"
End If
Return result
End Function
End Structure
Private Function SatisfiesTypeConstraint(
typeArgument As TypeSymbol,
constraintType As TypeSymbol,
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
) As Boolean
If constraintType.IsErrorType() Then
constraintType.AddUseSiteInfo(useSiteInfo)
Return False
End If
Return Conversions.HasWideningDirectCastConversionButNotEnumTypeConversion(typeArgument, constraintType, useSiteInfo)
End Function
' See Bindable::ValidateNewConstraintForType.
Private Function SatisfiesConstructorConstraint(
typeParameter As TypeParameterSymbol,
typeArgument As TypeSymbol,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo)) As Boolean
Debug.Assert(typeParameter.HasConstructorConstraint)
Select Case typeArgument.TypeKind
Case TypeKind.Enum
Return True
Case TypeKind.TypeParameter
If DirectCast(typeArgument, TypeParameterSymbol).HasConstructorConstraint OrElse typeArgument.IsValueType Then
Return True
Else
If diagnosticsBuilder IsNot Nothing Then
' "Type parameter '{0}' must have either a 'New' constraint or a 'Structure' constraint to satisfy the 'New' constraint for type parameter '{1}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_BadGenericParamForNewConstraint2, typeArgument, typeParameter)))
End If
Return False
End If
Case Else
Dim isStructure As Boolean = typeArgument.TypeKind = TypeKind.Structure
Dim constraintError = ConstructorConstraintError.NoPublicParameterlessConstructor
If typeArgument.TypeKind = TypeKind.Class OrElse isStructure Then
Dim namedType = DirectCast(typeArgument, NamedTypeSymbol)
constraintError = HasPublicParameterlessConstructor(namedType)
If constraintError = ConstructorConstraintError.None AndAlso namedType.IsMustInherit Then
constraintError = ConstructorConstraintError.MustInheritType
End If
End If
If diagnosticsBuilder IsNot Nothing Then
Select Case constraintError
Case ConstructorConstraintError.NoPublicParameterlessConstructor
' "Type argument '{0}' must have a public parameterless instance constructor to satisfy the 'New' constraint for type parameter '{1}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_NoSuitableNewForNewConstraint2, typeArgument, typeParameter)))
Case ConstructorConstraintError.MustInheritType
' "Type argument '{0}' is declared 'MustInherit' and does not satisfy the 'New' constraint for type parameter '{1}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_MustInheritForNewConstraint2, typeArgument, typeParameter)))
Case ConstructorConstraintError.HasRequiredMembers
' '{2}' cannot satisfy the 'New' constraint on parameter '{1}' in the generic type or or method '{0}' because '{2}' has required members.
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_NewConstraintCannotHaveRequiredMembers, typeParameter.ContainingSymbol, typeParameter, typeArgument)))
Case ConstructorConstraintError.None
Case Else
Throw ExceptionUtilities.UnexpectedValue(constraintError)
End Select
End If
Return constraintError = ConstructorConstraintError.None
End Select
End Function
' See Bindable::ValidateReferenceConstraintForType.
Private Function SatisfiesReferenceTypeConstraint(
typeParameter As TypeParameterSymbol,
typeArgument As TypeSymbol,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo)) As Boolean
Debug.Assert((typeParameter Is Nothing) OrElse typeParameter.HasReferenceTypeConstraint)
If Not typeArgument.IsReferenceType Then
If diagnosticsBuilder IsNot Nothing Then
' "Type argument '{0}' does not satisfy the 'Class' constraint for type parameter '{1}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_BadTypeArgForRefConstraint2, typeArgument, typeParameter)))
End If
Return False
End If
Return True
End Function
' See Bindable::ValidateValueConstraintForType.
Private Function SatisfiesValueTypeConstraint(
constructedSymbol As Symbol,
typeParameter As TypeParameterSymbol,
typeArgument As TypeSymbol,
diagnosticsBuilder As ArrayBuilder(Of TypeParameterDiagnosticInfo),
<[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Debug.Assert((typeParameter Is Nothing) OrElse typeParameter.HasValueTypeConstraint)
If Not typeArgument.IsValueType Then
If diagnosticsBuilder IsNot Nothing Then
Dim containingType = TryCast(constructedSymbol, TypeSymbol)
If (containingType IsNot Nothing) AndAlso containingType.IsNullableType() Then
' "Type '{0}' must be a value type or a type argument constrained to 'Structure' in order to be used with 'Nullable' or nullable modifier '?'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_BadTypeArgForStructConstraintNull, typeArgument)))
Else
' "Type argument '{0}' does not satisfy the 'Structure' constraint for type parameter '{1}'."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_BadTypeArgForStructConstraint2, typeArgument, typeParameter)))
End If
End If
Return False
ElseIf IsNullableTypeOrTypeParameter(typeArgument, useSiteInfo) Then
If diagnosticsBuilder IsNot Nothing Then
' "'System.Nullable' does not satisfy the 'Structure' constraint for type parameter '{0}'. Only non-nullable 'Structure' types are allowed."
diagnosticsBuilder.Add(New TypeParameterDiagnosticInfo(typeParameter, ErrorFactory.ErrorInfo(ERRID.ERR_NullableDisallowedForStructConstr1, typeParameter)))
End If
Return False
End If
Return True
End Function
' See Bindable::ConstraintsConflict.
Private Function HasConflict(constraint1 As TypeParameterConstraint, constraint2 As TypeParameterConstraint, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Dim constraintType1 = constraint1.TypeConstraint
Dim constraintType2 = constraint2.TypeConstraint
If (constraintType1 IsNot Nothing) AndAlso (constraintType1.IsInterfaceType()) Then
Return False
End If
If (constraintType2 IsNot Nothing) AndAlso (constraintType2.IsInterfaceType()) Then
Return False
End If
If constraint1.IsValueTypeConstraint Then
If HasValueTypeConstraintConflict(constraint2, useSiteInfo) Then
Return True
End If
ElseIf constraint2.IsValueTypeConstraint Then
If HasValueTypeConstraintConflict(constraint1, useSiteInfo) Then
Return True
End If
End If
If constraint1.IsReferenceTypeConstraint Then
If HasReferenceTypeConstraintConflict(constraint2) Then
Return True
End If
ElseIf constraint2.IsReferenceTypeConstraint Then
If HasReferenceTypeConstraintConflict(constraint1) Then
Return True
End If
End If
If (constraintType1 IsNot Nothing) AndAlso
(constraintType2 IsNot Nothing) AndAlso
Not SatisfiesTypeConstraint(constraintType1, constraintType2, useSiteInfo) AndAlso
Not SatisfiesTypeConstraint(constraintType2, constraintType1, useSiteInfo) Then
Return True
End If
Return False
End Function
Private Function HasValueTypeConstraintConflict(constraint As TypeParameterConstraint, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
Dim constraintType = constraint.TypeConstraint
If constraintType Is Nothing Then
Return False
End If
If SatisfiesValueTypeConstraint(constructedSymbol:=Nothing, typeParameter:=Nothing, typeArgument:=constraintType,
diagnosticsBuilder:=Nothing,
useSiteInfo:=useSiteInfo) Then
Return False
End If
Select Case constraintType.SpecialType
Case SpecialType.System_Object, SpecialType.System_ValueType
Return False
End Select
Return True
End Function
Private Function HasReferenceTypeConstraintConflict(constraint As TypeParameterConstraint) As Boolean
Dim constraintType = constraint.TypeConstraint
If constraintType Is Nothing Then
Return False
End If
If SatisfiesReferenceTypeConstraint(typeParameter:=Nothing, typeArgument:=constraintType, diagnosticsBuilder:=Nothing) Then
Return False
End If
Return True
End Function
Private Function IsNullableTypeOrTypeParameter(type As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As Boolean
If type.TypeKind = TypeKind.TypeParameter Then
Dim typeParameter = DirectCast(type, TypeParameterSymbol)
Dim constraintTypes = typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(useSiteInfo)
For Each constraintType In constraintTypes
If IsNullableTypeOrTypeParameter(constraintType, useSiteInfo) Then
Return True
End If
Next
Return False
Else
Return type.IsNullableType()
End If
End Function
Private Function GetConstraintCycleInfo(cycle As ConsList(Of TypeParameterSymbol)) As CompoundDiagnosticInfo
Debug.Assert(cycle.Any())
Dim previous As TypeParameterSymbol = Nothing
Dim builder = ArrayBuilder(Of DiagnosticInfo).GetInstance()
builder.Add(Nothing) ' Placeholder for first entry added later.
For Each typeParameter In cycle
If previous IsNot Nothing Then
builder.Add(ErrorFactory.ErrorInfo(ERRID.ERR_ConstraintCycleLink2, typeParameter, previous))
End If
previous = typeParameter
Next
builder(0) = ErrorFactory.ErrorInfo(ERRID.ERR_ConstraintCycleLink2, cycle.Head, previous)
Dim diagnostics = builder.ToArrayAndFree()
Array.Reverse(diagnostics)
Return New CompoundDiagnosticInfo(diagnostics)
End Function
Public Enum ConstructorConstraintError
None
NoPublicParameterlessConstructor
MustInheritType
HasRequiredMembers
End Enum
''' <summary>
''' Return true if the class type has a public parameterless constructor.
''' </summary>
Public Function HasPublicParameterlessConstructor(type As NamedTypeSymbol) As ConstructorConstraintError
type = type.OriginalDefinition
Debug.Assert(type.TypeKind = TypeKind.Class OrElse type.TypeKind = TypeKind.Structure)
Dim sourceClass = If(type.TypeKind = TypeKind.Class, TryCast(type, SourceNamedTypeSymbol), Nothing)
If sourceClass IsNot Nothing AndAlso Not sourceClass.MembersHaveBeenCreated Then
' When we are dealing with group classes and synthetic entry points,
' we can end up here while we are building the set of members for the type.
' Using InstanceConstructors property will send us into an infinite loop.
If sourceClass.InferFromSyntaxIfClassWillHavePublicParameterlessConstructor() Then
Return ConstructorConstraintError.None
Else
Return ConstructorConstraintError.NoPublicParameterlessConstructor
End If
End If
Dim hasRequiredMembers = type.AllRequiredMembers.Count > 0 OrElse type.HasRequiredMembersError
For Each constructor In type.InstanceConstructors
If constructor.ParameterCount = 0 Then
If constructor.DeclaredAccessibility <> Accessibility.Public Then
Return ConstructorConstraintError.NoPublicParameterlessConstructor
ElseIf hasRequiredMembers AndAlso Not constructor.HasSetsRequiredMembers Then
Return ConstructorConstraintError.HasRequiredMembers
Else
Return ConstructorConstraintError.None
End If
End If
Next
If Not type.TypeKind = TypeKind.Structure Then
Return ConstructorConstraintError.NoPublicParameterlessConstructor
ElseIf hasRequiredMembers Then
Return ConstructorConstraintError.HasRequiredMembers
Else
Return ConstructorConstraintError.None
End If
End Function
''' <summary>
''' Return true if the constraints collection contains the given type constraint.
''' </summary>
Private Function ContainsTypeConstraint(constraints As ArrayBuilder(Of TypeParameterConstraint), constraintType As TypeSymbol) As Boolean
Debug.Assert(constraintType IsNot Nothing)
For Each constraint In constraints
Dim type = constraint.TypeConstraint
If (type IsNot Nothing) AndAlso constraintType.IsSameTypeIgnoringAll(type) Then
Return True
End If
Next
Return False
End Function
Private Function RequiresChecking(type As NamedTypeSymbol) As Boolean
If type.Arity = 0 Then
Return False
End If
' If type is the original definition, there is no need
' to check constraints. In the following for instance:
' Class A(Of T As Structure)
' Dim F As New A(Of T)()
' End Class
If type.OriginalDefinition Is type Then
Return False
End If
Debug.Assert(Not TypeSymbol.Equals(type.ConstructedFrom, type, TypeCompareKind.ConsiderEverything))
Return True
End Function
Private Function RequiresChecking(method As MethodSymbol) As Boolean
If Not method.IsGenericMethod Then
Return False
End If
' If method is the original definition, there is no need
' to check constraints. In the following for instance:
' Sub M(Of T As Class)()
' M(Of T)()
' End Function
If method.OriginalDefinition Is method Then
Return False
End If
Debug.Assert(method.ConstructedFrom <> method)
Return True
End Function
End Module
End Namespace
|