static string TypeId(TypeSymbol type) { string id; if (type is ArrayTypeSymbol) { var arrtype = (ArrayTypeSymbol)type; id = TypeId(arrtype.ElementType) + "[]"; // TODO: MDSize } else if (type.IsTypeParameter()) { id = type.MetadataName; } else if (type.ContainingType != null) // nested type { id = TypeId(type.ContainingType) + "." + TypeNameId(type); } else { var ns = ((NamedTypeSymbol)type.OriginalDefinition).NamespaceName.Replace("<", "<").Replace(">", ">"); var name = TypeNameId(type); id = string.IsNullOrEmpty(ns) ? name : (ns + "." + name); } // return(id); }
/// <summary> /// </summary> /// <param name="typeSymbol"> /// </param> /// <param name="genericContext"> /// </param> /// <returns> /// </returns> internal static IType ResolveGeneric(this TypeSymbol typeSymbol, IGenericContext genericContext, bool isByRef = false, bool isPinned = false) { if (genericContext != null && !genericContext.IsEmpty) { if (typeSymbol.IsTypeParameter()) { return(genericContext.ResolveTypeParameter(new MetadataTypeAdapter(typeSymbol, isByRef, isPinned), isByRef, isPinned)); } var arrayType = typeSymbol as ArrayTypeSymbol; if (arrayType != null) { return(arrayType.ElementType.ResolveGeneric(genericContext, isByRef, isPinned).ToArrayType(arrayType.Rank)); } var namedTypeSymbol = typeSymbol as NamedTypeSymbol; if (namedTypeSymbol != null) { var metadataType = new MetadataTypeAdapter(namedTypeSymbol, isByRef, isPinned); if (metadataType.IsGenericTypeDefinition && !genericContext.IsEmpty) { return(new MetadataTypeAdapter(namedTypeSymbol, genericContext, isByRef, isPinned)); } } } return(new MetadataTypeAdapter(typeSymbol, genericContext, isByRef, isPinned)); }
internal ImmutableArray <CustomModifier> SubstituteCustomModifiers(TypeSymbol type, ImmutableArray <CustomModifier> customModifiers) { if (type.IsTypeParameter()) { return(new TypeWithModifiers(type, customModifiers).SubstituteType(this).CustomModifiers); } return(SubstituteCustomModifiers(customModifiers)); }
private bool AnyConstraintTypes( ImmutableArray <TypeWithAnnotations> constraintTypes, Func <TypeParameterSymbol, bool> predicate1, Func <TypeSymbol, bool> predicate2) { if (constraintTypes.IsEmpty) { return(false); } bool result = false; ConsList <TypeParameterSymbol> inProgress = ConsList <TypeParameterSymbol> .Empty.Push(this); var stack = ArrayBuilder <TypeWithAnnotations> .GetInstance(constraintTypes.Length); stack.AddRange(constraintTypes); do { TypeWithAnnotations constraintType = stack.Pop(); TypeSymbol type = constraintType.IsResolved ? constraintType.Type : constraintType.DefaultType; if (type.IsTypeParameter()) { var typeParameter = (TypeParameterSymbol)type; if (inProgress.ContainsReference(typeParameter)) { continue; } if (predicate1(typeParameter)) { result = true; break; } inProgress = inProgress.Prepend(typeParameter); stack.AddRange(typeParameter.GetConstraintTypesNoUseSiteDiagnostics(early: true)); } else if (predicate2(type)) { result = true; break; } }while (stack.Count != 0); stack.Free(); return(result); }
private static bool IsPossiblyByRefTypeParameter(TypeSymbol type) { if (type.IsTypeParameter()) { return(true); } if (type.IsErrorType()) { var byRefReturnType = type as ByRefReturnErrorTypeSymbol; return(((object)byRefReturnType != null) && byRefReturnType.ReferencedType.IsTypeParameter()); } return(false); }
public static void AddTypesParticipatingInUserDefinedConversion( ArrayBuilder <NamedTypeSymbol> result, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo ) { if ((object)type == null) { return; } // CONSIDER: These sets are usually small; if they are large then this is an O(n^2) // CONSIDER: algorithm. We could use a hash table instead to build up the set. Debug.Assert(!type.IsTypeParameter()); // optimization: bool excludeExisting = result.Count > 0; if (type.IsClassType() || type.IsStructType()) { var namedType = (NamedTypeSymbol)type; if (!excludeExisting || !HasIdentityConversionToAny(namedType, result)) { result.Add(namedType); } } if (!includeBaseTypes) { return; } NamedTypeSymbol t = type.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo); while ((object)t != null) { if (!excludeExisting || !HasIdentityConversionToAny(t, result)) { result.Add(t); } t = t.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo); } }
public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder <NamedTypeSymbol> result, TypeSymbol type, bool includeBaseTypes, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { if ((object)type == null) { return; } // CONSIDER: These sets are usually small; if they are large then this is an O(n^2) // CONSIDER: algorithm. We could use a hash table instead to build up the set. Debug.Assert(!type.IsTypeParameter()); // optimization: bool excludeExisting = result.Count > 0; // The decimal type does not contribute its user-defined conversions to the mix; though its // conversions are actually implemented via user-defined operators, we logically treat it as // though those conversions were built-in. if (type.IsClassType() || type.IsStructType() && type.SpecialType != SpecialType.System_Decimal) { var namedType = (NamedTypeSymbol)type; if (!excludeExisting || !HasIdentityConversionToAny(namedType, result)) { result.Add(namedType); } } if (!includeBaseTypes) { return; } NamedTypeSymbol t = type.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); while ((object)t != null) { if (!excludeExisting || !HasIdentityConversionToAny(t, result)) { result.Add(t); } t = t.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } }
public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder<NamedTypeSymbol> result, TypeSymbol type, bool includeBaseTypes, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { if ((object)type == null) { return; } // CONSIDER: These sets are usually small; if they are large then this is an O(n^2) // CONSIDER: algorithm. We could use a hash table instead to build up the set. Debug.Assert(!type.IsTypeParameter()); // optimization: bool excludeExisting = result.Count > 0; // The decimal type does not contribute its user-defined conversions to the mix; though its // conversions are actually implemented via user-defined operators, we logically treat it as // though those conversions were built-in. if (type.IsClassType() || type.IsStructType() && type.SpecialType != SpecialType.System_Decimal) { var namedType = (NamedTypeSymbol)type; if (!excludeExisting || !HasIdentityConversionToAny(namedType, result)) { result.Add(namedType); } } if (!includeBaseTypes) { return; } NamedTypeSymbol t = type.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); while ((object)t != null) { if (!excludeExisting || !HasIdentityConversionToAny(t, result)) { result.Add(t); } t = t.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } }
private static TypeSymbol GetUnderlyingEffectiveType(TypeSymbol type, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // Spec 6.4.4: User-defined implicit conversions // Spec 6.4.5: User-defined explicit conversions // // Determine the types S0 and T0. // * If S or T are nullable types, let Su and Tu be their underlying types, otherwise let Su and Tu be S and T, respectively. // * If Su or Tu are type parameters, S0 and T0 are their effective base types, otherwise S0 and T0 are equal to Su and Tu, respectively. if ((object)type != null) { type = type.StrippedType(); if (type.IsTypeParameter()) { type = ((TypeParameterSymbol)type).EffectiveBaseClass(ref useSiteDiagnostics); } } return(type); }
private static TypeSymbol GetUnderlyingEffectiveType(TypeSymbol type, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // Spec 6.4.4: User-defined implicit conversions // Spec 6.4.5: User-defined explicit conversions // // Determine the types S0 and T0. // * If S or T are nullable types, let Su and Tu be their underlying types, otherwise let Su and Tu be S and T, respectively. // * If Su or Tu are type parameters, S0 and T0 are their effective base types, otherwise S0 and T0 are equal to Su and Tu, respectively. if ((object)type != null) { type = type.StrippedType(); if (type.IsTypeParameter()) { type = ((TypeParameterSymbol)type).EffectiveBaseClass(ref useSiteDiagnostics); } } return type; }
public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder<NamedTypeSymbol> result, TypeSymbol type, bool includeBaseTypes, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { if ((object)type == null) { return; } // CONSIDER: These sets are usually small; if they are large then this is an O(n^2) // CONSIDER: algorithm. We could use a hash table instead to build up the set. Debug.Assert(!type.IsTypeParameter()); // optimization: bool excludeExisting = result.Count > 0; if (type.IsClassType() || type.IsStructType()) { var namedType = (NamedTypeSymbol)type; if (!excludeExisting || !HasIdentityConversionToAny(namedType, result)) { result.Add(namedType); } } if (!includeBaseTypes) { return; } NamedTypeSymbol t = type.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); while ((object)t != null) { if (!excludeExisting || !HasIdentityConversionToAny(t, result)) { result.Add(t); } t = t.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } }
private bool GetUserDefinedOperators( BinaryOperatorKind kind, TypeSymbol type0, BoundExpression left, BoundExpression right, ArrayBuilder<BinaryOperatorAnalysisResult> results, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // Spec 7.3.5 Candidate user-defined operators // SPEC: Given a type T and an operation operator op(A), where op is an overloadable // SPEC: operator and A is an argument list, the set of candidate user-defined operators // SPEC: provided by T for operator op(A) is determined as follows: // SPEC: Determine the type T0. If T is a nullable type, T0 is its underlying type, // SPEC: otherwise T0 is equal to T. // (The caller has already passed in the stripped type.) // SPEC: For all operator op declarations in T0 and all lifted forms of such operators, // SPEC: if at least one operator is applicable (7.5.3.1) with respect to the argument // SPEC: list A, then the set of candidate operators consists of all such applicable // SPEC: operators in T0. Otherwise, if T0 is object, the set of candidate operators is empty. // SPEC: Otherwise, the set of candidate operators provided by T0 is the set of candidate // SPEC: operators provided by the direct base class of T0, or the effective base class of // SPEC: T0 if T0 is a type parameter. string name = OperatorFacts.BinaryOperatorNameFromOperatorKind(kind); var operators = ArrayBuilder<BinaryOperatorSignature>.GetInstance(); bool hadApplicableCandidates = false; NamedTypeSymbol current = type0 as NamedTypeSymbol; if ((object)current == null) { current = type0.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } if ((object)current == null && type0.IsTypeParameter()) { current = ((TypeParameterSymbol)type0).EffectiveBaseClass(ref useSiteDiagnostics); } for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { operators.Clear(); GetUserDefinedBinaryOperatorsFromType(current, kind, name, operators); results.Clear(); if (CandidateOperators(operators, left, right, results, ref useSiteDiagnostics)) { hadApplicableCandidates = true; break; } } operators.Free(); return hadApplicableCandidates; }
// Returns true if there were any applicable candidates. private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(operand != null); if ((object)operand.Type == null) { // If the operand has no type -- because it is a null reference or a lambda or a method group -- // there is no way we can determine what type to search for user-defined operators. return(false); } // Spec 7.3.5 Candidate user-defined operators // SPEC: Given a type T and an operation op(A) ... the set of candidate user-defined // SPEC: operators provided by T for op(A) is determined as follows: // SPEC: If T is a nullable type then T0 is its underlying type; otherwise T0 is T. // SPEC: For all operator declarations in T0 and all lifted forms of such operators, if // SPEC: at least one operator is applicable with respect to A then the set of candidate // SPEC: operators consists of all such applicable operators. Otherwise, if T0 is object // SPEC: then the set of candidate operators is empty. Otherwise, the set of candidate // SPEC: operators is the set provided by the direct base class of T0, or the effective // SPEC: base class of T0 if T0 is a type parameter. TypeSymbol type0 = operand.Type.StrippedType(); // Searching for user-defined operators is expensive; let's take an early out if we can. if (OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0)) { return(false); } string name = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind); var operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance(); bool hadApplicableCandidates = false; NamedTypeSymbol current = type0 as NamedTypeSymbol; if ((object)current == null) { current = type0.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } if ((object)current == null && type0.IsTypeParameter()) { current = ((TypeParameterSymbol)type0).EffectiveBaseClass(ref useSiteDiagnostics); } for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { operators.Clear(); GetUserDefinedUnaryOperatorsFromType(current, kind, name, operators); results.Clear(); if (CandidateOperators(operators, operand, results, ref useSiteDiagnostics)) { hadApplicableCandidates = true; break; } } operators.Free(); return(hadApplicableCandidates); }
internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, TypeSymbol rightType, bool rightIsNull, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // SPEC: The predefined reference type equality operators require one of the following: // SPEC: (1) Both operands are a value of a type known to be a reference-type or the literal null. // SPEC: Furthermore, an explicit reference conversion exists from the type of either // SPEC: operand to the type of the other operand. Or: // SPEC: (2) One operand is a value of type T where T is a type-parameter and the other operand is // SPEC: the literal null. Furthermore T does not have the value type constraint. // SPEC ERROR: Notice that the spec calls out that an explicit reference conversion must exist; // SPEC ERROR: in fact it should say that an explicit reference conversion, implicit reference // SPEC ERROR: conversion or identity conversion must exist. The conversion from object to object // SPEC ERROR: is not classified as a reference conversion at all; it is an identity conversion. // Dev10 does not follow the spec exactly for type parameters. Specifically, in Dev10, // if a type parameter argument is known to be a value type, or if a type parameter // argument is not known to be either a value type or reference type and the other // argument is not null, reference type equality cannot be applied. Otherwise, the // effective base class of the type parameter is used to determine the conversion // to the other argument type. (See ExpressionBinder::GetRefEqualSigs.) if (((object)leftType != null) && leftType.IsTypeParameter()) { if (leftType.IsValueType || (!leftType.IsReferenceType && !rightIsNull)) { return(false); } leftType = ((TypeParameterSymbol)leftType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)leftType != null); } if (((object)rightType != null) && rightType.IsTypeParameter()) { if (rightType.IsValueType || (!rightType.IsReferenceType && !leftIsNull)) { return(false); } rightType = ((TypeParameterSymbol)rightType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)rightType != null); } var leftIsReferenceType = ((object)leftType != null) && leftType.IsReferenceType; if (!leftIsReferenceType && !leftIsNull) { return(false); } var rightIsReferenceType = ((object)rightType != null) && rightType.IsReferenceType; if (!rightIsReferenceType && !rightIsNull) { return(false); } // If at least one side is null then clearly a conversion exists. if (leftIsNull || rightIsNull) { return(true); } var leftConversion = Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics); if (leftConversion.IsIdentity || leftConversion.IsReference) { return(true); } var rightConversion = Conversions.ClassifyConversion(rightType, leftType, ref useSiteDiagnostics); if (rightConversion.IsIdentity || rightConversion.IsReference) { return(true); } return(false); }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> /// <param name="t1">LHS</param> /// <param name="t2">RHS</param> /// <param name="substitution"> /// Substitutions performed so far (or null for none). /// Keys are type parameters, values are types (possibly type parameters). /// Will be updated with new subsitutions by the callee. /// Should be ignored when false is returned. /// </param> /// <returns>True if there exists a type map such that Map(LHS) == Map(RHS).</returns> /// <remarks> /// Derived from Dev10's BSYMMGR::UnifyTypes. /// Two types will not unify if they have different custom modifiers. /// </remarks> private static bool CanUnifyHelper(TypeSymbol t1, TypeSymbol t2, ref MutableTypeMap substitution) { if (ReferenceEquals(t1, t2)) { return true; } else if ((object)t1 == null || (object)t2 == null) { // Can't both be null or they would have been ReferenceEquals return false; } if (substitution != null) { t1 = substitution.SubstituteType(t1); t2 = substitution.SubstituteType(t2); } // If one of the types is a type parameter, then the substitution could make them ReferenceEquals. if (ReferenceEquals(t1, t2)) { return true; } // We can avoid a lot of redundant checks if we ensure that we only have to check // for type parameters on the LHS if (!t1.IsTypeParameter() && t2.IsTypeParameter()) { TypeSymbol tmp = t1; t1 = t2; t2 = tmp; } // If t1 is not a type parameter, then neither is t2 Debug.Assert(t1.IsTypeParameter() || !t2.IsTypeParameter()); switch (t1.Kind) { case SymbolKind.ArrayType: { if (t2.TypeKind != t1.TypeKind) { return false; } ArrayTypeSymbol at1 = (ArrayTypeSymbol)t1; ArrayTypeSymbol at2 = (ArrayTypeSymbol)t2; if (at1.Rank != at2.Rank || !at1.CustomModifiers.SequenceEqual(at2.CustomModifiers)) { return false; } return CanUnifyHelper(at1.ElementType, at2.ElementType, ref substitution); } case SymbolKind.PointerType: { if (t2.TypeKind != t1.TypeKind) { return false; } PointerTypeSymbol pt1 = (PointerTypeSymbol)t1; PointerTypeSymbol pt2 = (PointerTypeSymbol)t2; if (!pt1.CustomModifiers.SequenceEqual(pt2.CustomModifiers)) { return false; } return CanUnifyHelper(pt1.PointedAtType, pt2.PointedAtType, ref substitution); } case SymbolKind.NamedType: case SymbolKind.ErrorType: { if (t2.TypeKind != t1.TypeKind) { return false; } NamedTypeSymbol nt1 = (NamedTypeSymbol)t1; NamedTypeSymbol nt2 = (NamedTypeSymbol)t2; if (!nt1.IsGenericType) { return !nt2.IsGenericType && nt1 == nt2; } else if (!nt2.IsGenericType) { return false; } int arity = nt1.Arity; if (nt2.Arity != arity || nt2.OriginalDefinition != nt1.OriginalDefinition) { return false; } for (int i = 0; i < arity; i++) { if (!CanUnifyHelper(nt1.TypeArgumentsNoUseSiteDiagnostics[i], nt2.TypeArgumentsNoUseSiteDiagnostics[i], ref substitution)) { return false; } } // Note: Dev10 folds this into the loop since GetTypeArgsAll includes type args for containing types return (object)nt1.ContainingType == null || CanUnifyHelper(nt1.ContainingType, nt2.ContainingType, ref substitution); } case SymbolKind.TypeParameter: { // These substitutions are not allowed in C# if (t2.TypeKind == TypeKind.Pointer || t2.SpecialType == SpecialType.System_Void) { return false; } TypeParameterSymbol tp1 = (TypeParameterSymbol)t1; // Perform the "occurs check" - i.e. ensure that t2 doesn't contain t1 to avoid recursive types // Note: t2 can't be the same type param - we would have caught that with ReferenceEquals above if (Contains(t2, tp1)) { return false; } if (substitution == null) { substitution = new MutableTypeMap(); } // MutableTypeMap.Add will throw if the key has already been added. However, // if t1 was already in the substitution, it would have been substituted at the // start of this method and we wouldn't be here. substitution.Add(tp1, t2); return true; } default: { return t1 == t2; } } }
internal static ConstantValue GetAsOperatorConstantResult(TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue) { // NOTE: Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue // NOTE: (they are non-constant expressions according to Section 7.19 of the specification), // NOTE: we want to perform constant analysis of is/as expressions during binding to generate warnings (always true/false/null) // NOTE: and during rewriting for optimized codegen. // Native compiler port: // // check for case we know is always false // if (arg->isNull() || !canCast(arg, type2, NOUDC) && type1->IsValType() && type2->isClassType() && (!type1->IsTypeParameterType() || !type2->isPredefType(PT_ENUM))) // { // GetErrorContext()->Error(tree, WRN_AlwaysNull, type2); // return rval; // } if (operandConstantValue == ConstantValue.Null || (conversionKind == ConversionKind.NoConversion && (operandType.IsValueType && targetType.IsClassType() && (!operandType.IsTypeParameter() || targetType.SpecialType != SpecialType.System_Enum)))) { return ConstantValue.Null; } else { return null; } }
private static bool IsPossiblyByRefTypeParameter(TypeSymbol type) { if (type.IsTypeParameter()) { return true; } if (type.IsErrorType()) { //var byRefReturnType = type as ByRefReturnErrorTypeSymbol; //return ((object)byRefReturnType != null) && byRefReturnType.ReferencedType.IsTypeParameter(); throw new NotImplementedException(); } return false; }
private bool GetUserDefinedOperators(UnaryOperatorKind kind, BoundExpression operand, ArrayBuilder <UnaryOperatorAnalysisResult> results, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(operand != null); if ((object)operand.Type == null) { // If the operand has no type -- because it is a null reference or a lambda or a method group -- // there is no way we can determine what type to search for user-defined operators. return(false); } // Spec 7.3.5 Candidate user-defined operators // SPEC: Given a type T and an operation op(A) ... the set of candidate user-defined // SPEC: operators provided by T for op(A) is determined as follows: // SPEC: If T is a nullable type then T0 is its underlying type; otherwise T0 is T. // SPEC: For all operator declarations in T0 and all lifted forms of such operators, if // SPEC: at least one operator is applicable with respect to A then the set of candidate // SPEC: operators consists of all such applicable operators. Otherwise, if T0 is object // SPEC: then the set of candidate operators is empty. Otherwise, the set of candidate // SPEC: operators is the set provided by the direct base class of T0, or the effective // SPEC: base class of T0 if T0 is a type parameter. // https://github.com/dotnet/roslyn/issues/34451: The spec quote should be adjusted to cover operators from interfaces as well. // From https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-06-27.md: // - We only even look for operator implementations in interfaces if one of the operands has a type that is an interface or // a type parameter with a non-empty effective base interface list. // - The applicable operators from classes / structs shadow those in interfaces.This matters for constrained type parameters: // the effective base class can shadow operators from effective base interfaces. // - If we find an applicable candidate in an interface, that candidate shadows all applicable operators in base interfaces: // we stop looking. TypeSymbol type0 = operand.Type.StrippedType(); // Searching for user-defined operators is expensive; let's take an early out if we can. if (OperatorFacts.DefinitelyHasNoUserDefinedOperators(type0)) { return(false); } string name = OperatorFacts.UnaryOperatorNameFromOperatorKind(kind); var operators = ArrayBuilder <UnaryOperatorSignature> .GetInstance(); bool hadApplicableCandidates = false; NamedTypeSymbol current = type0 as NamedTypeSymbol; if ((object)current == null) { current = type0.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } if ((object)current == null && type0.IsTypeParameter()) { current = ((TypeParameterSymbol)type0).EffectiveBaseClass(ref useSiteDiagnostics); } for (; (object)current != null; current = current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { operators.Clear(); GetUserDefinedUnaryOperatorsFromType(current, kind, name, operators); results.Clear(); if (CandidateOperators(operators, operand, results, ref useSiteDiagnostics)) { hadApplicableCandidates = true; break; } } // Look in base interfaces, or effective interfaces for type parameters if (!hadApplicableCandidates) { ImmutableArray <NamedTypeSymbol> interfaces = default; if (type0.IsInterfaceType()) { interfaces = type0.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } else if (type0.IsTypeParameter()) { interfaces = ((TypeParameterSymbol)type0).AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } if (!interfaces.IsDefaultOrEmpty) { var shadowedInterfaces = PooledHashSet <NamedTypeSymbol> .GetInstance(); var resultsFromInterface = ArrayBuilder <UnaryOperatorAnalysisResult> .GetInstance(); results.Clear(); foreach (NamedTypeSymbol @interface in interfaces) { if ([email protected]) { // this code could be reachable in error situations continue; } if (shadowedInterfaces.Contains(@interface)) { // this interface is "shadowed" by a derived interface continue; } operators.Clear(); resultsFromInterface.Clear(); GetUserDefinedUnaryOperatorsFromType(@interface, kind, name, operators); if (CandidateOperators(operators, operand, resultsFromInterface, ref useSiteDiagnostics)) { hadApplicableCandidates = true; results.AddRange(resultsFromInterface); // this interface "shadows" all its base interfaces shadowedInterfaces.AddAll(@interface.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)); } } shadowedInterfaces.Free(); resultsFromInterface.Free(); } } operators.Free(); return(hadApplicableCandidates); }
internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, TypeSymbol rightType, bool rightIsNull, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // SPEC: The predefined reference type equality operators require one of the following: // SPEC: (1) Both operands are a value of a type known to be a reference-type or the literal null. // SPEC: Furthermore, an explicit reference conversion exists from the type of either // SPEC: operand to the type of the other operand. Or: // SPEC: (2) One operand is a value of type T where T is a type-parameter and the other operand is // SPEC: the literal null. Furthermore T does not have the value type constraint. // SPEC ERROR: Notice that the spec calls out that an explicit reference conversion must exist; // SPEC ERROR: in fact it should say that an explicit reference conversion, implicit reference // SPEC ERROR: conversion or identity conversion must exist. The conversion from object to object // SPEC ERROR: is not classified as a reference conversion at all; it is an identity conversion. // Dev10 does not follow the spec exactly for type parameters. Specifically, in Dev10, // if a type parameter argument is known to be a value type, or if a type parameter // argument is not known to be either a value type or reference type and the other // argument is not null, reference type equality cannot be applied. Otherwise, the // effective base class of the type parameter is used to determine the conversion // to the other argument type. (See ExpressionBinder::GetRefEqualSigs.) if (((object)leftType != null) && leftType.IsTypeParameter()) { if (leftType.IsValueType || (!leftType.IsReferenceType && !rightIsNull)) { return false; } leftType = ((TypeParameterSymbol)leftType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)leftType != null); } if (((object)rightType != null) && rightType.IsTypeParameter()) { if (rightType.IsValueType || (!rightType.IsReferenceType && !leftIsNull)) { return false; } rightType = ((TypeParameterSymbol)rightType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)rightType != null); } var leftIsReferenceType = ((object)leftType != null) && leftType.IsReferenceType; if (!leftIsReferenceType && !leftIsNull) { return false; } var rightIsReferenceType = ((object)rightType != null) && rightType.IsReferenceType; if (!rightIsReferenceType && !rightIsNull) { return false; } // If at least one side is null then clearly a conversion exists. if (leftIsNull || rightIsNull) { return true; } var leftConversion = Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics); if (leftConversion.IsIdentity || leftConversion.IsReference) { return true; } var rightConversion = Conversions.ClassifyConversion(rightType, leftType, ref useSiteDiagnostics); if (rightConversion.IsIdentity || rightConversion.IsReference) { return true; } return false; }
internal ImmutableArray<CustomModifier> SubstituteCustomModifiers(TypeSymbol type, ImmutableArray<CustomModifier> customModifiers) { if (type.IsTypeParameter()) { return new TypeWithModifiers(type, customModifiers).SubstituteType(this).CustomModifiers; } return SubstituteCustomModifiers(customModifiers); }
/// <summary> /// Determine whether there is any substitution of type parameters that will /// make two types identical. /// </summary> /// <param name="t1">LHS</param> /// <param name="t2">RHS</param> /// <param name="substitution"> /// Substitutions performed so far (or null for none). /// Keys are type parameters, values are types (possibly type parameters). /// Will be updated with new subsitutions by the callee. /// Should be ignored when false is returned. /// </param> /// <returns>True if there exists a type map such that Map(LHS) == Map(RHS).</returns> /// <remarks> /// Derived from Dev10's BSYMMGR::UnifyTypes. /// Two types will not unify if they have different custom modifiers. /// </remarks> private static bool CanUnifyHelper(TypeSymbol t1, TypeSymbol t2, ref MutableTypeMap substitution) { if (ReferenceEquals(t1, t2)) { return(true); } else if ((object)t1 == null || (object)t2 == null) { // Can't both be null or they would have been ReferenceEquals return(false); } if (substitution != null) { t1 = substitution.SubstituteType(t1); t2 = substitution.SubstituteType(t2); } // If one of the types is a type parameter, then the substitution could make them ReferenceEquals. if (ReferenceEquals(t1, t2)) { return(true); } // We can avoid a lot of redundant checks if we ensure that we only have to check // for type parameters on the LHS if (!t1.IsTypeParameter() && t2.IsTypeParameter()) { TypeSymbol tmp = t1; t1 = t2; t2 = tmp; } // If t1 is not a type parameter, then neither is t2 Debug.Assert(t1.IsTypeParameter() || !t2.IsTypeParameter()); switch (t1.Kind) { case SymbolKind.ArrayType: { if (t2.TypeKind != t1.TypeKind) { return(false); } ArrayTypeSymbol at1 = (ArrayTypeSymbol)t1; ArrayTypeSymbol at2 = (ArrayTypeSymbol)t2; if (at1.Rank != at2.Rank || !at1.CustomModifiers.SequenceEqual(at2.CustomModifiers)) { return(false); } return(CanUnifyHelper(at1.ElementType, at2.ElementType, ref substitution)); } case SymbolKind.PointerType: { if (t2.TypeKind != t1.TypeKind) { return(false); } PointerTypeSymbol pt1 = (PointerTypeSymbol)t1; PointerTypeSymbol pt2 = (PointerTypeSymbol)t2; if (!pt1.CustomModifiers.SequenceEqual(pt2.CustomModifiers)) { return(false); } return(CanUnifyHelper(pt1.PointedAtType, pt2.PointedAtType, ref substitution)); } case SymbolKind.NamedType: case SymbolKind.ErrorType: { if (t2.TypeKind != t1.TypeKind) { return(false); } NamedTypeSymbol nt1 = (NamedTypeSymbol)t1; NamedTypeSymbol nt2 = (NamedTypeSymbol)t2; if (!nt1.IsGenericType) { return(!nt2.IsGenericType && nt1 == nt2); } else if (!nt2.IsGenericType) { return(false); } int arity = nt1.Arity; if (nt2.Arity != arity || nt2.OriginalDefinition != nt1.OriginalDefinition) { return(false); } for (int i = 0; i < arity; i++) { if (!CanUnifyHelper(nt1.TypeArgumentsNoUseSiteDiagnostics[i], nt2.TypeArgumentsNoUseSiteDiagnostics[i], ref substitution)) { return(false); } } // Note: Dev10 folds this into the loop since GetTypeArgsAll includes type args for containing types return((object)nt1.ContainingType == null || CanUnifyHelper(nt1.ContainingType, nt2.ContainingType, ref substitution)); } case SymbolKind.TypeParameter: { // These substitutions are not allowed in C# if (t2.TypeKind == TypeKind.Pointer || t2.SpecialType == SpecialType.System_Void) { return(false); } TypeParameterSymbol tp1 = (TypeParameterSymbol)t1; // Perform the "occurs check" - i.e. ensure that t2 doesn't contain t1 to avoid recursive types // Note: t2 can't be the same type param - we would have caught that with ReferenceEquals above if (Contains(t2, tp1)) { return(false); } if (substitution == null) { substitution = new MutableTypeMap(); } // MutableTypeMap.Add will throw if the key has already been added. However, // if t1 was already in the substitution, it would have been substituted at the // start of this method and we wouldn't be here. substitution.Add(tp1, t2); return(true); } default: { return(t1 == t2); } } }
private static bool IsPossiblyByRefTypeParameter(TypeSymbol type) { if (type.IsTypeParameter()) { return true; } if (type.IsErrorType()) { var byRefReturnType = type as ByRefReturnErrorTypeSymbol; return ((object)byRefReturnType != null) && byRefReturnType.ReferencedType.IsTypeParameter(); } return false; }
public CommonConversion ClassifyConversion(TypeSymbol from, TypeSymbol to, ConversionKind kinds) { if (from == to) { return(IdentityConversion); } // implicit conversions handled by 'EmitConversion': if (to.SpecialType == SpecialType.System_Void) { return(IdentityConversion); } // object cast possible implicitly: if ((kinds & ConversionKind.Reference) == ConversionKind.Reference) { if (from.IsReferenceType && to.IsReferenceType && from.IsOfType(to)) { // (PHP) string, resource, array, alias -> object: NoConversion if (to.SpecialType != SpecialType.System_Object || !IsSpecialReferenceType(from)) { return(ReferenceConversion); } } if (to.SpecialType == SpecialType.System_Object && (from.IsInterfaceType() || (from.IsReferenceType && from.IsTypeParameter()))) { return(ReferenceConversion); } } // resolve conversion operator method: if ((kinds & ConversionKind.Numeric) == ConversionKind.Numeric) { var conv = ClassifyNumericConversion(from, to); if (conv.Exists) { return(conv); } } // strict: if ((kinds & ConversionKind.Strict) == ConversionKind.Strict) { var op = ResolveOperator(from, false, ImplicitConversionOpNames(to), new[] { _compilation.CoreTypes.StrictConvert.Symbol }, target: to); if (op != null) { return(new CommonConversion(true, false, false, false, true, op)); } } // implicit if ((kinds & ConversionKind.Implicit) == ConversionKind.Implicit) { var op = TryWellKnownImplicitConversion(from, to) ?? ResolveOperator(from, false, ImplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to); if (op != null) { return(new CommonConversion(true, false, false, false, true, op)); } } // explicit: if ((kinds & ConversionKind.Explicit) == ConversionKind.Explicit) { var op = ResolveOperator(from, false, ExplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to); if (op != null) { return(new CommonConversion(true, false, false, false, false, op)); } // explicit reference conversion (reference type -> reference type) else if ( from.IsReferenceType && to.IsReferenceType && !IsSpecialReferenceType(from) && !IsSpecialReferenceType(to) && !from.IsArray() && !to.IsArray()) { return(ExplicitReferenceConversion); } } // return(NoConversion); }
private void EmitDefaultValue(TypeSymbol type, bool used, SyntaxNode syntaxNode) { if (used) { // default type parameter values must be emitted as 'initobj' regardless of constraints if (!type.IsTypeParameter()) { var constantValue = type.GetDefaultValue(); if (constantValue != null) { _builder.EmitConstantValue(constantValue); return; } } EmitInitObj(type, true, syntaxNode); } }
public CommonConversion ClassifyConversion(TypeSymbol from, TypeSymbol to, bool checkimplicit = true, bool checkexplicit = true) { if (from == to) { return(IdentityConversion); } if (from.IsReferenceType && to.IsReferenceType && from.IsOfType(to)) { // (PHP) string, resource, array, alias -> object: NoConversion if (to.SpecialType != SpecialType.System_Object || !IsSpecialReferenceType(from)) { return(ReferenceConversion); } } if (to.SpecialType == SpecialType.System_Object && (from.IsInterfaceType() || (from.IsReferenceType && from.IsTypeParameter()))) { return(ReferenceConversion); } // implicit conversions handled by 'EmitConversion': if (to.SpecialType == SpecialType.System_Void) { return(IdentityConversion); } // resolve conversion operator method: var conv = ClassifyNumericConversion(from, to); if (!conv.Exists) { // TODO: cache result var op = checkimplicit ? TryWellKnownImplicitConversion(from, to) ?? ResolveOperator(from, false, ImplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to) : null; if (op != null) { conv = new CommonConversion(true, false, false, false, true, op); } else if (checkexplicit) { op = ResolveOperator(from, false, ExplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to); if (op != null) { conv = new CommonConversion(true, false, false, false, false, op); } // explicit reference conversion (reference type -> reference type) else if ( from.IsReferenceType && to.IsReferenceType && !IsSpecialReferenceType(from) && !IsSpecialReferenceType(to) && !from.IsArray() && !to.IsArray()) { conv = ExplicitReferenceConversion; } } } return(conv); }