// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// /// Represents a type parameter in a generic type or generic method. /// internal abstract partial class TypeParameterSymbol : TypeSymbol, ITypeParameterSymbol { /// /// The original definition of this symbol. If this symbol is constructed from another /// symbol by type substitution then OriginalDefinition gets the original symbol as it was defined in /// source or metadata. /// public new virtual TypeParameterSymbol OriginalDefinition { get { return this; } } protected override sealed TypeSymbol OriginalTypeSymbolDefinition { get { return this.OriginalDefinition; } } /// /// If this is a type parameter of a reduced extension method, gets the type parameter definition that /// this type parameter was reduced from. Otherwise, returns Nothing. /// public virtual TypeParameterSymbol ReducedFrom { get { return null; } } /// /// The ordinal position of the type parameter in the parameter list which declares /// it. The first type parameter has ordinal zero. /// public abstract int Ordinal { // This is needed to determine hiding in C#: // // interface IB { void M(C x); } // interface ID : IB { new void M(C x); } // // ID.M hides IB.M even though their formal parameters have different // types. When comparing formal parameter types for hiding purposes we must // compare method type parameters by ordinal, not by identity. get; } internal virtual DiagnosticInfo GetConstraintsUseSiteErrorInfo() { return null; } /// /// The types that were directly specified as constraints on the type parameter. /// Duplicates and cycles are removed, although the collection may include /// redundant constraints where one constraint is a base type of another. /// internal ImmutableArray ConstraintTypesNoUseSiteDiagnostics { get { this.EnsureAllConstraintsAreResolved(); return this.GetConstraintTypes(ConsList.Empty); } } internal ImmutableArray ConstraintTypesWithDefinitionUseSiteDiagnostics(ref HashSet useSiteDiagnostics) { var result = ConstraintTypesNoUseSiteDiagnostics; AppendConstraintsUseSiteErrorInfo(ref useSiteDiagnostics); foreach (var constraint in result) { ((TypeSymbol)constraint.Type.OriginalDefinition).AddUseSiteDiagnostics(ref useSiteDiagnostics); } return result; } private void AppendConstraintsUseSiteErrorInfo(ref HashSet useSiteDiagnostics) { DiagnosticInfo errorInfo = this.GetConstraintsUseSiteErrorInfo(); if ((object)errorInfo != null) { if (useSiteDiagnostics == null) { useSiteDiagnostics = new HashSet(); } useSiteDiagnostics.Add(errorInfo); } } /// /// True if the parameterless constructor constraint was specified for the type parameter. /// public abstract bool HasConstructorConstraint { get; } /// /// The type parameter kind of this type parameter. /// public abstract TypeParameterKind TypeParameterKind { get; } /// /// The method that declared this type parameter, or null. /// public MethodSymbol DeclaringMethod { get { return this.ContainingSymbol as MethodSymbol; } } /// /// The type that declared this type parameter, or null. /// public NamedTypeSymbol DeclaringType { get { return this.ContainingSymbol as NamedTypeSymbol; } } // Type parameters do not have members public sealed override ImmutableArray GetMembers() { return ImmutableArray.Empty; } // Type parameters do not have members public sealed override ImmutableArray GetMembers(string name) { return ImmutableArray.Empty; } // Type parameters do not have members public sealed override ImmutableArray GetTypeMembers() { return ImmutableArray.Empty; } // Type parameters do not have members public sealed override ImmutableArray GetTypeMembers(string name) { return ImmutableArray.Empty; } // Type parameters do not have members public sealed override ImmutableArray GetTypeMembers(string name, int arity) { return ImmutableArray.Empty; } internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument argument) { return visitor.VisitTypeParameter(this, argument); } public override void Accept(CSharpSymbolVisitor visitor) { visitor.VisitTypeParameter(this); } public override TResult Accept(CSharpSymbolVisitor visitor) { return visitor.VisitTypeParameter(this); } public sealed override SymbolKind Kind { get { return SymbolKind.TypeParameter; } } public sealed override TypeKind TypeKind { get { return TypeKind.TypeParameter; } } // Only the compiler can create TypeParameterSymbols. internal TypeParameterSymbol() { } public sealed override Accessibility DeclaredAccessibility { get { return Accessibility.NotApplicable; } } public sealed override bool IsStatic { get { return false; } } public sealed override bool IsAbstract { get { return false; } } public sealed override bool IsSealed { get { return false; } } internal sealed override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics => null; internal sealed override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList basesBeingResolved = null) { return ImmutableArray.Empty; } protected override ImmutableArray GetAllInterfaces() { return ImmutableArray.Empty; } /// /// The effective base class of the type parameter (spec 10.1.5). If the deduced /// base type is a reference type, the effective base type will be the same as /// the deduced base type. Otherwise if the deduced base type is a value type, /// the effective base type will be the most derived reference type from which /// deduced base type is derived. /// internal NamedTypeSymbol EffectiveBaseClassNoUseSiteDiagnostics { get { this.EnsureAllConstraintsAreResolved(); return this.GetEffectiveBaseClass(ConsList.Empty); } } internal NamedTypeSymbol EffectiveBaseClass(ref HashSet useSiteDiagnostics) { AppendConstraintsUseSiteErrorInfo(ref useSiteDiagnostics); var result = EffectiveBaseClassNoUseSiteDiagnostics; if ((object)result != null) { result.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); } return result; } /// /// The effective interface set (spec 10.1.5). /// internal ImmutableArray EffectiveInterfacesNoUseSiteDiagnostics { get { this.EnsureAllConstraintsAreResolved(); return this.GetInterfaces(ConsList.Empty); } } /// /// The most encompassed type (spec 6.4.2) from the constraints. /// internal TypeSymbol DeducedBaseTypeNoUseSiteDiagnostics { get { this.EnsureAllConstraintsAreResolved(); return this.GetDeducedBaseType(ConsList.Empty); } } internal TypeSymbol DeducedBaseType(ref HashSet useSiteDiagnostics) { AppendConstraintsUseSiteErrorInfo(ref useSiteDiagnostics); var result = DeducedBaseTypeNoUseSiteDiagnostics; if ((object)result != null) { ((TypeSymbol)result.OriginalDefinition).AddUseSiteDiagnostics(ref useSiteDiagnostics); } return result; } /// /// The effective interface set and any base interfaces of those /// interfaces. This is AllInterfaces excluding interfaces that are /// only implemented by the effective base type. /// internal ImmutableArray AllEffectiveInterfacesNoUseSiteDiagnostics { get { return base.GetAllInterfaces(); } } internal ImmutableArray AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref HashSet useSiteDiagnostics) { var result = AllEffectiveInterfacesNoUseSiteDiagnostics; // Since bases affect content of AllInterfaces set, we need to make sure they all are good. var current = DeducedBaseType(ref useSiteDiagnostics); while ((object)current != null) { current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } foreach (var iface in result) { iface.OriginalDefinition.AddUseSiteDiagnostics(ref useSiteDiagnostics); } return result; } /// /// Called by , , , and . /// to allow derived classes to ensure constraints within the containing /// type or method are resolved in a consistent order, regardless of the /// order the callers query individual type parameters. /// internal abstract void EnsureAllConstraintsAreResolved(); /// /// Helper method to force type parameter constraints to be resolved. /// protected static void EnsureAllConstraintsAreResolved(ImmutableArray typeParameters) { foreach (var typeParameter in typeParameters) { // Invoke any method that forces constraints to be resolved. var unused = typeParameter.GetConstraintTypes(ConsList.Empty); } } internal abstract ImmutableArray GetConstraintTypes(ConsList inProgress); internal abstract ImmutableArray GetInterfaces(ConsList inProgress); internal abstract NamedTypeSymbol GetEffectiveBaseClass(ConsList inProgress); internal abstract TypeSymbol GetDeducedBaseType(ConsList inProgress); private static bool ConstraintImpliesReferenceType(TypeSymbol constraint) { if (constraint.TypeKind == TypeKind.TypeParameter) { return IsReferenceTypeFromConstraintTypes(((TypeParameterSymbol)constraint).ConstraintTypesNoUseSiteDiagnostics); } else if (!constraint.IsReferenceType) { return false; } else { switch (constraint.TypeKind) { case TypeKind.Interface: return false; // can be satisfied by value types case TypeKind.Error: return false; } switch (constraint.SpecialType) { case SpecialType.System_Object: case SpecialType.System_ValueType: case SpecialType.System_Enum: return false; // can be satisfied by value types } return true; } } // From typedesc.cpp : // > A recursive helper that helps determine whether this variable is constrained as ObjRef. // > Please note that we do not check the gpReferenceTypeConstraint special constraint here // > because this property does not propagate up the constraining hierarchy. // > (e.g. "class A where S : T, where T : class" does not guarantee that S is ObjRef) internal static bool IsReferenceTypeFromConstraintTypes(ImmutableArray constraintTypes) { foreach (var constraintType in constraintTypes) { if (ConstraintImpliesReferenceType(constraintType.Type)) { return true; } } return false; } internal static bool? IsNotNullableIfReferenceTypeFromConstraintTypes(ImmutableArray constraintTypes) { Debug.Assert(!constraintTypes.IsDefaultOrEmpty); bool? result = false; foreach (TypeWithAnnotations constraintType in constraintTypes) { bool? fromType = IsNotNullableIfReferenceTypeFromConstraintType(constraintType); if (fromType == true) { return true; } else if (fromType == null) { result = null; } } return result; } internal static bool? IsNotNullableIfReferenceTypeFromConstraintType(TypeWithAnnotations constraintType) { if (constraintType.NullableAnnotation.IsAnnotated()) { return false; } if (constraintType.TypeKind == TypeKind.TypeParameter) { bool? isNotNullableIfReferenceType = ((TypeParameterSymbol)constraintType.Type).IsNotNullableIfReferenceType; if (isNotNullableIfReferenceType == false) { return false; } else if (isNotNullableIfReferenceType == null) { return null; } } if (constraintType.NullableAnnotation.IsOblivious()) { return null; } return true; } internal static bool IsValueTypeFromConstraintTypes(ImmutableArray constraintTypes) { foreach (var constraintType in constraintTypes) { if (constraintType.Type.IsValueType) { return true; } } return false; } public sealed override bool IsReferenceType { get { if (this.HasReferenceTypeConstraint) { return true; } return IsReferenceTypeFromConstraintTypes(this.ConstraintTypesNoUseSiteDiagnostics); } } protected bool? CalculateIsNotNullableIfReferenceType() { bool? fromReferenceTypeConstraint = false; if (this.HasReferenceTypeConstraint) { fromReferenceTypeConstraint = !this.ReferenceTypeConstraintIsNullable; if (fromReferenceTypeConstraint == true) { return true; } } ImmutableArray constraintTypes = this.ConstraintTypesNoUseSiteDiagnostics; if (constraintTypes.IsEmpty) { return fromReferenceTypeConstraint; } bool? fromTypes = IsNotNullableIfReferenceTypeFromConstraintTypes(constraintTypes); if (fromTypes == true || fromReferenceTypeConstraint == false) { return fromTypes; } Debug.Assert(fromReferenceTypeConstraint == null); Debug.Assert(fromTypes != true); return null; } // https://github.com/dotnet/roslyn/issues/26198 Should this API be exposed through ITypeParameterSymbol? internal abstract bool? IsNotNullableIfReferenceType { get; } public sealed override bool IsValueType { get { if (this.HasValueTypeConstraint) { return true; } return IsValueTypeFromConstraintTypes(this.ConstraintTypesNoUseSiteDiagnostics); } } internal sealed override ManagedKind ManagedKind { get { return HasUnmanagedTypeConstraint ? ManagedKind.Unmanaged : ManagedKind.Managed; } } public sealed override bool IsRefLikeType { get { return false; } } public sealed override bool IsReadOnly { get { // even if T is indirectly constrained to a struct, // we only can use members via constrained calls, so "true" would have no effect return false; } } internal sealed override ObsoleteAttributeData ObsoleteAttributeData { get { return null; } } public abstract bool HasReferenceTypeConstraint { get; } /// /// Returns whether the reference type constraint (the 'class' constraint) should also be treated as nullable ('class?') or non-nullable (class!). /// In some cases this aspect is unknown (null value is returned). For example, when 'class' constraint is specified in a NonNullTypes(false) context. /// This API returns false when is false. /// internal abstract bool? ReferenceTypeConstraintIsNullable { get; } public abstract bool HasValueTypeConstraint { get; } public abstract bool HasUnmanagedTypeConstraint { get; } public abstract VarianceKind Variance { get; } internal sealed override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet checkedTypes) { return false; } internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison, IReadOnlyDictionary isValueTypeOverrideOpt = null) { return this.Equals(t2 as TypeParameterSymbol, comparison, isValueTypeOverrideOpt); } internal bool Equals(TypeParameterSymbol other) { return Equals(other, TypeCompareKind.ConsiderEverything); } private bool Equals(TypeParameterSymbol other, TypeCompareKind comparison, IReadOnlyDictionary isValueTypeOverrideOpt) { if (ReferenceEquals(this, other)) { return true; } if ((object)other == null || !ReferenceEquals(other.OriginalDefinition, this.OriginalDefinition)) { return false; } // Type parameters may be equal but not reference equal due to independent alpha renamings. return other.ContainingSymbol.ContainingType.Equals(this.ContainingSymbol.ContainingType, comparison, isValueTypeOverrideOpt); } public override int GetHashCode() { return Hash.Combine(ContainingSymbol, Ordinal); } internal override void AddNullableTransforms(ArrayBuilder transforms) { } internal override bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray transforms, ref int position, out TypeSymbol result) { result = this; return true; } internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) { return this; } internal override TypeSymbol MergeNullability(TypeSymbol other, VarianceKind variance) { Debug.Assert(this.Equals(other, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); return this; } #region ITypeParameterTypeSymbol Members #pragma warning disable IDE0055 // Fix formatting. This formatting is correct, need 16.1 for the updated formatter to not flag CodeAnalysis.NullableAnnotation ITypeParameterSymbol.ReferenceTypeConstraintNullableAnnotation => ReferenceTypeConstraintIsNullable switch { false when !HasReferenceTypeConstraint => CodeAnalysis.NullableAnnotation.NotApplicable, false => CodeAnalysis.NullableAnnotation.NotAnnotated, true => CodeAnalysis.NullableAnnotation.Annotated, null => CodeAnalysis.NullableAnnotation.Disabled, }; #pragma warning restore IDE0055 // Fix formatting TypeParameterKind ITypeParameterSymbol.TypeParameterKind { get { return (TypeParameterKind)this.TypeParameterKind; } } IMethodSymbol ITypeParameterSymbol.DeclaringMethod { get { return this.DeclaringMethod; } } INamedTypeSymbol ITypeParameterSymbol.DeclaringType { get { return this.DeclaringType; } } ImmutableArray ITypeParameterSymbol.ConstraintTypes { get { return this.ConstraintTypesNoUseSiteDiagnostics.SelectAsArray(c => (ITypeSymbol)c.Type); } } ImmutableArray ITypeParameterSymbol.ConstraintNullableAnnotations { get => this.ConstraintTypesNoUseSiteDiagnostics.SelectAsArray(c => c.NullableAnnotation.ToPublicAnnotation()); } ITypeParameterSymbol ITypeParameterSymbol.OriginalDefinition { get { return this.OriginalDefinition; } } ITypeParameterSymbol ITypeParameterSymbol.ReducedFrom { get { return this.ReducedFrom; } } #endregion #region ISymbol Members public override void Accept(SymbolVisitor visitor) { visitor.VisitTypeParameter(this); } public override TResult Accept(SymbolVisitor visitor) { return visitor.VisitTypeParameter(this); } #endregion } }