static void addFromClassOrStruct(ArrayBuilder <TypeSymbol> result, bool excludeExisting, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo) { if (type.IsClassType() || type.IsStructType()) { if (!excludeExisting || !HasIdentityConversionToAny(type, result)) { result.Add(type); } } 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 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); } }
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); } }
/// <summary> /// Returns true if the members of superType are accessible from subType due to inheritance. /// </summary> public static bool IsAccessibleViaInheritance(this TypeSymbol superType, TypeSymbol subType, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // NOTE: we don't use strict inheritance. Instead we ignore constructed generic types // and only consider the unconstructed types. Ecma-334, 4th edition contained the // following text supporting this (although, for instance members) in 10.5.3 Protected // access for instance members: // In the context of generics (25.1.6), the rules for accessing protected and // protected internal instance members are augmented by the following: // o Within a generic class G, access to an inherited protected instance member M // using a primary-expression of the form E.M is permitted if the type of E is a // class type constructed from G or a class type derived from a class type // constructed from G. // This text is missing in the current version of the spec, but we believe this is accidental. var originalSuperType = superType.OriginalDefinition; for (TypeSymbol current = subType; (object)current != null; current = (current.Kind == SymbolKind.TypeParameter) ? ((TypeParameterSymbol)current).EffectiveBaseClass(ref useSiteDiagnostics) : current.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics)) { if (ReferenceEquals(current.OriginalDefinition, originalSuperType)) { return(true); } } // The method returns true for superType == subType. // Two different submission type symbols semantically represent a single type, so we should also return true. return(superType.TypeKind == TypeKind.Submission && subType.TypeKind == TypeKind.Submission); }
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); }
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); }
private bool UpperBoundClassInference(NamedTypeSymbol source, TypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert((object)source != null); Debug.Assert((object)target != null); if (source.TypeKind != TypeKind.Class || target.TypeKind != TypeKind.Class) { return false; } // SPEC: * Otherwise, if U is a class type C<U1...Uk> and V is a class type which // SPEC: inherits directly or indirectly from C<V1...Vk> then an exact // SPEC: inference is made from each Ui to the corresponding Vi. var targetBase = target.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); while ((object)targetBase != null) { if (targetBase.OriginalDefinition == source.OriginalDefinition) { ExactTypeArgumentInference(source, targetBase, ref useSiteDiagnostics); return true; } targetBase = targetBase.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } return false; }
private bool LowerBoundClassInference(TypeSymbol source, NamedTypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert((object)source != null); Debug.Assert((object)target != null); if (target.TypeKind != TypeKind.Class) { return false; } // Spec: 7.5.2.9 Lower-bound interfaces // SPEC: * Otherwise, if V is a class type C<V1...Vk> and U is a class type which // SPEC: inherits directly or indirectly from C<U1...Uk> // SPEC: then an exact inference is made from each Ui to the corresponding Vi. // SPEC: * Otherwise, if V is a class type C<V1...Vk> and U is a type parameter // SPEC: with effective base class C<U1...Uk> // SPEC: then an exact inference is made from each Ui to the corresponding Vi. // SPEC: * Otherwise, if V is a class type C<V1...Vk> and U is a type parameter // SPEC: with an effective base class which inherits directly or indirectly from // SPEC: C<U1...Uk> then an exact inference is made // SPEC: from each Ui to the corresponding Vi. NamedTypeSymbol sourceBase = null; if (source.TypeKind == TypeKind.Class) { sourceBase = source.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } else if (source.TypeKind == TypeKind.TypeParameter) { sourceBase = ((TypeParameterSymbol)source).EffectiveBaseClass(ref useSiteDiagnostics); } while ((object)sourceBase != null) { if (sourceBase.OriginalDefinition == target.OriginalDefinition) { ExactTypeArgumentInference(sourceBase, target, ref useSiteDiagnostics); return true; } sourceBase = sourceBase.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); } return false; }