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);
                }
            }
Exemplo n.º 2
0
        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);
            }
        }
Exemplo n.º 3
0
        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);
            }
        }
Exemplo n.º 5
0
        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);
            }
        }
Exemplo n.º 6
0
        internal static ConstantValue GetIsOperatorConstantResult(TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue)
        {
            Debug.Assert((object)targetType != null);

            // SPEC:    The result of the operation depends on D and T as follows:
            // SPEC:    1)	If T is a reference type, the result is true if D and T are the same type, if D is a reference type and
            // SPEC:        an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists.
            // SPEC:    2)	If T is a nullable type, the result is true if D is the underlying type of T.
            // SPEC:    3)	If T is a non-nullable value type, the result is true if D and T are the same type.
            // SPEC:    4)	Otherwise, the result is false.

            // NOTE:    The language specification talks about the runtime evaluation of the is operation.
            // NOTE:    However, we are interested in computing the compile time constant value for the expression.
            // 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.
            // NOTE: 
            // NOTE:    Because the heuristic presented here is used to change codegen, it must be conservative. It is acceptable
            // NOTE:    for us to fail to report a warning in cases where humans could logically deduce that the operator will
            // NOTE:    always return false. It is not acceptable to inaccurately warn that the operator will always return false
            // NOTE:    if there are cases where it might succeed.
            // 

            // To begin our heuristic: if the operand is literal null then we automatically return that the
            // result is false. You might think that we can simply check to see if the conversion is 
            // ConversionKind.NullConversion, but "null is T" for a type parameter T is actually classified
            // as an implicit reference conversion if T is constrained to reference types. Rather
            // than deal with all those special cases we can simply bail out here.

            if (operandConstantValue == ConstantValue.Null)
            {
                return ConstantValue.False;
            }

            Debug.Assert((object)operandType != null);

            switch (conversionKind)
            {
                case ConversionKind.NoConversion:
                    // Oddly enough, "x is T" can be true even if there is no conversion from x to T! 
                    //
                    // Scenario 1: Type parameter compared to System.Enum.
                    //
                    // bool M1<X>(X x) where X : struct { return x is Enum; }
                    //
                    // There is no conversion from X to Enum, not even an explicit conversion. But
                    // nevertheless, X could be constructed as an enumerated type.
                    // However, we can sometimes know that the result will be false.
                    //
                    // Scenario 2: Constrained type parameter compared to reference type.
                    //
                    // bool M2<X>(X x) where X : struct { return x is string; }
                    //
                    // We know that X, constrained to struct, will never be string.
                    //
                    // Scenario 3: Value type compared to type parameter.
                    //
                    // bool M3<T>(int x) { return x is T; }
                    //
                    // There is no conversion from int to T, but T could nevertheless be int.
                    //
                    // Scenario 4: Constructed type compared to open type
                    //
                    // bool M4<T>(C<int> x) { return x is C<T>; } 
                    //
                    // There is no conversion from C<int> to C<T>, but nevertheless, T might be int.
                    //
                    // Scenario 5: Open type compared to constructed type:
                    //
                    // bool M5<X>(C<X> x) { return x is C<int>);
                    //
                    // Again, X could be int.
                    // 
                    // We could then go on to get more complicated. For example, 
                    //
                    // bool M6<X>(C<X> x) where X : struct { return x is C<string>; }
                    //
                    // We know that C<X> is never convertible to C<string> no matter what
                    // X is. Or:
                    //
                    // bool M7<T>(Dictionary<int, int> x) { return x is List<T>; }
                    //
                    // We know that no matter what T is, the conversion will never succeed.
                    //
                    // As noted above, we must be conservative. We follow the lead of the native compiler,
                    // which uses the following algorithm:
                    //
                    // * If neither type is open and there is no conversion then the result is always false:

                    if (!operandType.ContainsTypeParameter() && !targetType.ContainsTypeParameter())
                    {
                        return ConstantValue.False;
                    }

                    // * Otherwise, at least one of them is of an open type. If the operand is of value type 
                    //   and the target is a class type other than System.Enum, then we are in scenario 2, 
                    //   not scenario 1, and can correctly deduce that the result is false.

                    if (operandType.IsValueType && targetType.IsClassType() && targetType.SpecialType != SpecialType.System_Enum)
                    {
                        return ConstantValue.False;
                    }

                    // * Otherwise, we give up. Though there are other situations in which we can deduce that
                    //   the result will always be false, such as scenarios 6 and 7, but we do not attempt
                    //   to deduce this.

                    // CONSIDER: we could use TypeUnification.CanUnify to do additional compile-time checking.

                    return null;

                case ConversionKind.ImplicitNumeric:
                case ConversionKind.ExplicitNumeric:
                case ConversionKind.ImplicitEnumeration:
                // case ConversionKind.ExplicitEnumeration: // Handled separately below.
                case ConversionKind.ImplicitConstant:
                case ConversionKind.ImplicitUserDefined:
                case ConversionKind.ExplicitUserDefined:
                case ConversionKind.IntPtr:

                    // Consider all the cases where we know that "x is T" must be false just from
                    // the conversion classification.
                    //
                    // If we have "x is T" and the conversion from x to T is numeric or enum then the result must be false.
                    //
                    // If we have "null is T" then obviously that must be false.
                    //
                    // If we have "1 is long" then that must be false. (If we have "1 is int" then it is an identity conversion,
                    // not an implicit constant conversion.
                    //
                    // User-defined and IntPtr conversions are always false for "is".

                    return ConstantValue.False;

                case ConversionKind.ExplicitEnumeration:
                    // Enum-to-enum conversions should be treated the same as unsuccessful struct-to-struct
                    // conversions (i.e. make allowances for type unification, etc)
                    if (operandType.IsEnumType() && targetType.IsEnumType())
                    {
                        goto case ConversionKind.NoConversion;
                    }

                    return ConstantValue.False;

                case ConversionKind.ExplicitNullable:

                    // An explicit nullable conversion is a conversion of one of the following forms:
                    //
                    // 1) X? --> Y?, where X --> Y is an explicit conversion.  (If X --> Y is an implicit
                    //    conversion then X? --> Y? is an implicit nullable conversion.) In this case we
                    //    know that "X? is Y?" must be false because either X? is null, or we have an
                    //    explicit conversion from struct type X to struct type Y, and so X is never of type Y.)
                    //
                    // 2) X --> Y?, where again, X --> Y is an explicit conversion. By the same reasoning
                    //    as in case 1, this must be false.

                    if (targetType.IsNullableType())
                    {
                        return ConstantValue.False;
                    }

                    Debug.Assert(operandType.IsNullableType());

                    // 3) X? --> X. In this case, this is just a different way of writing "x != null".
                    //    We do not know what the result will be.
                    //    CONSIDER: If we know statically that the operand is going to be null or non-null
                    //    CONSIDER: then we could give a better result here.

                    if (Conversions.HasIdentityConversion(operandType.GetNullableUnderlyingType(), targetType))
                    {
                        return null;
                    }

                    // 4) X? --> Y where the conversion X --> Y is an implicit or explicit value type conversion.
                    //    "X? is Y" again must be false.

                    return ConstantValue.False;

                case ConversionKind.ImplicitReference:
                case ConversionKind.ExplicitReference:
                case ConversionKind.Unboxing:
                    // In these three cases, the expression type must be a reference type. Therefore,
                    // the result cannot be determined. The expression could be null, resulting 
                    // in false, or it could be a non-null reference to the appropriate type,
                    // resulting in true.
                    return null;

                case ConversionKind.Identity:
                    // The result of "x is T" can be statically determined to be true if x is an expression 
                    // of non-nullable value type T. If x is of reference or nullable value type then
                    // we cannot know, because again, the expression value could be null or it could be good. 
                    // If it is of pointer type then we have already given an error.
                    return (operandType.IsValueType && !operandType.IsNullableType()) ? ConstantValue.True : null;

                case ConversionKind.Boxing:

                    // A boxing conversion might be a conversion:
                    //
                    // * From a non-nullable value type to a reference type
                    // * From a nullable value type to a reference type
                    // * From a type parameter that *could* be a value type under construction
                    //   to a reference type
                    //
                    // In the first case we know that the conversion will always succeed and that the
                    // operand is never null, and therefore "is" will always result in true. 
                    //
                    // In the second two cases we do not know; either the nullable value type could be
                    // null, or the type parameter could be constructed with a reference type, and it
                    // could be null.

                    return operandType.IsValueType && !operandType.IsNullableType() ? ConstantValue.True : null;

                case ConversionKind.ImplicitNullable:
                    // We have "x is T" in one of the following situations:
                    // 1) x is of type X and T is X?.  The value is always true.
                    // 2) x is of type X and T is Y? where X is convertible to Y via an implicit numeric conversion. Eg, 
                    //    x is of type int and T is decimal?.  The value is always false.
                    // 3) x is of type X? and T is Y? where X is convertible to Y via an implicit numeric conversion.
                    //    The value is always false.

                    Debug.Assert(targetType.IsNullableType());
                    return (operandType == targetType.GetNullableUnderlyingType()) ? ConstantValue.True : ConstantValue.False;

                default:
                case ConversionKind.ImplicitDynamic:
                case ConversionKind.ExplicitDynamic:
                case ConversionKind.PointerToInteger:
                case ConversionKind.PointerToPointer:
                case ConversionKind.PointerToVoid:
                case ConversionKind.IntegerToPointer:
                case ConversionKind.NullToPointer:
                case ConversionKind.AnonymousFunction:
                case ConversionKind.NullLiteral:
                case ConversionKind.MethodGroup:
                    // We've either replaced Dynamic with Object, or already bailed out with an error.
                    throw ExceptionUtilities.UnexpectedValue(conversionKind);
            }
        }
Exemplo n.º 7
0
        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;
            }
        }