public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder <TypeSymbol> result, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            // 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.

            // 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.

            // Spec 6.4.4: User-defined implicit conversions
            //   Find the set of types D from which user-defined conversion operators
            //   will be considered. This set consists of S0 (if S0 is a class or struct),
            //   the base classes of S0 (if S0 is a class), and T0 (if T0 is a class or struct).
            //
            // Spec 6.4.5: User-defined explicit conversions
            //   Find the set of types, D, from which user-defined conversion operators will be considered.
            //   This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class),
            //   T0 (if T0 is a class or struct), and the base classes of T0 (if T0 is a class).

            // https://github.com/dotnet/roslyn/issues/53798: Adjust the above specification quote appropriately.

            if ((object)type == null)
            {
                return;
            }

            type = type.StrippedType();

            // optimization:
            bool excludeExisting = result.Count > 0;

            if (type is TypeParameterSymbol typeParameter)
            {
                NamedTypeSymbol effectiveBaseClass = typeParameter.EffectiveBaseClass(ref useSiteInfo);

                addFromClassOrStruct(result, excludeExisting, effectiveBaseClass, includeBaseTypes, ref useSiteInfo);

                switch (effectiveBaseClass.SpecialType)
                {
                case SpecialType.System_ValueType:
                case SpecialType.System_Enum:
                case SpecialType.System_Array:
                case SpecialType.System_Object:
                    if (!excludeExisting || !HasIdentityConversionToAny(typeParameter, result))
                    {
                        // Add the type parameter to the set as well. This will be treated equivalent to adding its
                        // effective interfaces to the set. We are not doing that here because we still need to know
                        // the originating type parameter as "constrained to" type.
                        result.Add(typeParameter);
                    }
                    break;
                }
            }
            else
            {
                addFromClassOrStruct(result, excludeExisting, type, includeBaseTypes, ref useSiteInfo);
            }
Example #2
0
        internal static BoundExpression GiveTupleTypeToDefaultLiteralIfNeeded(BoundExpression expr, TypeSymbol targetType)
        {
            if (!expr.IsLiteralDefault() || targetType is null)
            {
                return(expr);
            }

            Debug.Assert(targetType.StrippedType().IsTupleType);
            return(new BoundDefaultExpression(expr.Syntax, targetType));
        }
            internal Multiple(ImmutableArray <TupleBinaryOperatorInfo> operators, TypeSymbol leftConvertedTypeOpt, TypeSymbol rightConvertedTypeOpt)
                : base(leftConvertedTypeOpt, rightConvertedTypeOpt)
            {
                Debug.Assert(leftConvertedTypeOpt is null || leftConvertedTypeOpt.StrippedType().IsTupleType);
                Debug.Assert(rightConvertedTypeOpt is null || rightConvertedTypeOpt.StrippedType().IsTupleType);
                Debug.Assert(!operators.IsDefault);
                Debug.Assert(operators.IsEmpty || operators.Length > 1); // an empty array is used for error cases, otherwise tuples must have cardinality > 1

                Operators = operators;
            }
Example #4
0
        /// <summary>
        /// Does an expression of type <paramref name="expressionType"/> "match" a pattern that looks for
        /// type <paramref name="patternType"/>?
        /// 'true' if the matched type catches all of them, 'false' if it catches none of them, and
        /// 'null' if it might catch some of them. For this test we assume the expression's value
        /// isn't null.
        /// </summary>
        protected bool?ExpressionOfTypeMatchesPatternType(
            TypeSymbol expressionType,
            TypeSymbol patternType,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            if (expressionType == patternType)
            {
                return(true);
            }

            var conversion = _conversions.ClassifyBuiltInConversion(expressionType, patternType, ref useSiteDiagnostics);

            // This is for classification purposes only; we discard use-site diagnostics. Use-site diagnostics will
            // be given if a conversion is actually used.
            switch (conversion.Kind)
            {
            case ConversionKind.Boxing:                 // a value of type int matches a pattern of type object
            case ConversionKind.Identity:               // a value of a given type matches a pattern of that type
            case ConversionKind.ImplicitReference:      // a value of type string matches a pattern of type object
                return(true);

            case ConversionKind.ImplicitNullable:       // a value of type int matches a pattern of type int?
            case ConversionKind.ExplicitNullable:       // a non-null value of type "int?" matches a pattern of type int
                // but if the types differ (e.g. one of them is type byte and the other is type int?).. no match
                return(ConversionsBase.HasIdentityConversion(expressionType.StrippedType().TupleUnderlyingTypeOrSelf(), patternType.StrippedType().TupleUnderlyingTypeOrSelf()));

            case ConversionKind.ExplicitEnumeration:    // a value of enum type does not match a pattern of integral type
            case ConversionKind.ExplicitNumeric:        // a value of type long does not match a pattern of type int
            case ConversionKind.ImplicitNumeric:        // a value of type short does not match a pattern of type int
            case ConversionKind.ImplicitTuple:          // distinct tuple types don't match
            case ConversionKind.NoConversion:
                return(false);

            case ConversionKind.ExplicitDynamic:        // a value of type dynamic might not match a pattern of type other than object
            case ConversionKind.ExplicitReference:      // a narrowing reference conversion might or might not succeed
            case ConversionKind.Unboxing:               // a value of type object might match a pattern of type int
                return(null);

            default:
                // other conversions don't apply (e.g. conversions from expression, user-defined) and should not arise
                throw ExceptionUtilities.UnexpectedValue(conversion.Kind);
            }
        }
Example #5
0
        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;
        }
Example #6
0
        private static int GetTupleCardinality(BoundExpression expr)
        {
            if (expr is BoundTupleExpression tuple)
            {
                return(tuple.Arguments.Length);
            }

            TypeSymbol type = expr.Type;

            if (type is null)
            {
                return(-1);
            }

            if (type.StrippedType() is { IsTupleType : true } tupleType)
            {
                return(tupleType.TupleElementTypesWithAnnotations.Length);
            }

            return(-1);
        }
Example #7
0
        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 MethodSymbol GetTruthOperator(TypeSymbol type, bool negative)
        {
            string name = negative ? WellKnownMemberNames.FalseOperatorName : WellKnownMemberNames.TrueOperatorName;
            var operators = ((NamedTypeSymbol)type.StrippedType()).GetOperators(name);
            Debug.Assert(!operators.IsEmpty);
            for (int i = 0; i < operators.Length; ++i)
            {
                Debug.Assert(operators[i].ParameterCount == 1);
                if (operators[i].ParameterTypes[0] == type)
                {
                    return operators[i];
                }
            }

            Debug.Assert(false, "How did we bind a user-defined logical operator or dynamic logical Boolean operator without operator false or operator true?");
            return null;
        }
        private bool IsCheckedConversion(TypeSymbol source, TypeSymbol target)
        {
            Debug.Assert((object)target != null);

            if ((object)source == null || !CheckOverflowAtRuntime)
            {
                return false;
            }

            if (source.IsDynamic())
            {
                return true;
            }

            SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
            SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;

            // integral to double or float is never checked, but float/double to integral 
            // may be checked.
            bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double;
            bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64;

            return
                sourceIsNumeric && (targetIsNumeric || target.IsPointerType()) ||
                targetIsNumeric && source.IsPointerType();
        }
Example #10
0
        private static Symbol GetIntrinsicOperatorSymbol(BinaryOperatorKind op, bool isDynamic, TypeSymbol leftType, TypeSymbol rightType, TypeSymbol returnType, bool isChecked)
        {
            if (!isDynamic)
            {
                leftType = leftType.StrippedType();
                rightType = rightType.StrippedType();
                returnType = returnType.StrippedType();
            }
            else
            {
                Debug.Assert(returnType.IsDynamic());

                if ((object)leftType == null)
                {
                    Debug.Assert(rightType.IsDynamic());
                    leftType = rightType;
                }
                else if ((object)rightType == null)
                {
                    Debug.Assert(leftType.IsDynamic());
                    rightType = leftType;
                }
            }
            return new SynthesizedIntrinsicOperatorSymbol(leftType,
                                                          OperatorFacts.BinaryOperatorNameFromOperatorKind(op),
                                                          rightType,
                                                          returnType,
                                                          isChecked);
        }
        private BoundExpression RewriteLiftedConversionInExpressionTree(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            bool @checked,
            bool explicitCastInCode,
            TypeSymbol rewrittenType)
        {
            Debug.Assert((object)rewrittenType != null);
            TypeSymbol rewrittenOperandType = rewrittenOperand.Type;
            Debug.Assert(rewrittenType.IsNullableType() || rewrittenOperandType.IsNullableType());

            TypeSymbol typeFrom = rewrittenOperandType.StrippedType();
            TypeSymbol typeTo = rewrittenType.StrippedType();
            if (typeFrom != typeTo && (typeFrom.SpecialType == SpecialType.System_Decimal || typeTo.SpecialType == SpecialType.System_Decimal))
            {
                // take special care if the underlying conversion is a decimal conversion
                TypeSymbol typeFromUnderlying = typeFrom;
                TypeSymbol typeToUnderlying = typeTo;

                // They can't both be enums, since one of them is decimal.
                if (typeFrom.IsEnumType())
                {
                    typeFromUnderlying = typeFrom.GetEnumUnderlyingType();

                    // NOTE: Dev10 converts enum? to underlying?, rather than directly to underlying.
                    rewrittenOperandType = rewrittenOperandType.IsNullableType() ? ((NamedTypeSymbol)rewrittenOperandType.OriginalDefinition).Construct(typeFromUnderlying) : typeFromUnderlying;
                    rewrittenOperand = BoundConversion.SynthesizedNonUserDefined(syntax, rewrittenOperand, ConversionKind.ImplicitEnumeration, rewrittenOperandType);
                }
                else if (typeTo.IsEnumType())
                {
                    typeToUnderlying = typeTo.GetEnumUnderlyingType();
                }

                var method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(DecimalConversionMethod(typeFromUnderlying, typeToUnderlying));
                conversionKind = conversionKind.IsImplicitConversion() ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined;
                var result = new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, default(ConstantValue), rewrittenType);
                return result;
            }
            else
            {
                return new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind), @checked, explicitCastInCode, default(ConstantValue), rewrittenType);
            }
        }
        // Determine if the conversion can actually overflow at runtime.  If not, no need to generate a checked instruction.
        private static bool NeedsChecked(TypeSymbol source, TypeSymbol target)
        {
            Debug.Assert((object)target != null);

            if ((object)source == null)
            {
                return false;
            }

            if (source.IsDynamic())
            {
                return true;
            }

            SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
            SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;

            // integral to double or float is never checked, but float/double to integral 
            // may be checked.
            bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double;
            bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64;
            return
                sourceIsNumeric && target.IsPointerType() ||
                targetIsNumeric && source.IsPointerType() ||
                sourceIsNumeric && targetIsNumeric && needsChecked[sourceST - SpecialType.System_Char, targetST - SpecialType.System_Char];
        }
        private static bool DistinctSpecialTypes(TypeSymbol source, TypeSymbol target)
        {
            Debug.Assert((object)target != null);

            if ((object)source == null)
            {
                return false;
            }

            SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType;
            SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType;
            return sourceST != targetST;
        }
        public static SpecialMember GetIntPtrConversionMethod(TypeSymbol source, TypeSymbol target)
        {
            Debug.Assert((object)source != null);
            Debug.Assert((object)target != null);

            TypeSymbol t0 = target.StrippedType();
            TypeSymbol s0 = source.StrippedType();

            SpecialType t0Type = t0.IsEnumType() ? t0.GetEnumUnderlyingType().SpecialType : t0.SpecialType;
            SpecialType s0Type = s0.IsEnumType() ? s0.GetEnumUnderlyingType().SpecialType : s0.SpecialType;

            if (t0Type == SpecialType.System_IntPtr)
            {
                if (source.TypeKind == TypeKind.PointerType)
                {
                    return SpecialMember.System_IntPtr__op_Explicit_FromPointer;
                }

                switch (s0Type)
                {
                    case SpecialType.System_Byte:
                    case SpecialType.System_SByte:
                    case SpecialType.System_Int16:
                    case SpecialType.System_UInt16:
                    case SpecialType.System_Char:
                    case SpecialType.System_Int32:
                        return SpecialMember.System_IntPtr__op_Explicit_FromInt32;
                    case SpecialType.System_UInt32:
                    case SpecialType.System_UInt64:
                    case SpecialType.System_Int64:
                    case SpecialType.System_Single:
                    case SpecialType.System_Double:
                    case SpecialType.System_Decimal:
                        return SpecialMember.System_IntPtr__op_Explicit_FromInt64;
                }
            }
            else if (t0Type == SpecialType.System_UIntPtr)
            {
                if (source.TypeKind == TypeKind.PointerType)
                {
                    return SpecialMember.System_UIntPtr__op_Explicit_FromPointer;
                }

                switch (s0Type)
                {
                    case SpecialType.System_Byte:
                    case SpecialType.System_UInt16:
                    case SpecialType.System_Char:
                    case SpecialType.System_UInt32:
                        return SpecialMember.System_UIntPtr__op_Explicit_FromUInt32;
                    case SpecialType.System_SByte:
                    case SpecialType.System_Int16:
                    case SpecialType.System_Int32:
                    case SpecialType.System_UInt64:
                    case SpecialType.System_Int64:
                    case SpecialType.System_Single:
                    case SpecialType.System_Double:
                    case SpecialType.System_Decimal:
                        return SpecialMember.System_UIntPtr__op_Explicit_FromUInt64;
                }
            }
            else if (s0Type == SpecialType.System_IntPtr)
            {
                if (target.TypeKind == TypeKind.PointerType)
                {
                    return SpecialMember.System_IntPtr__op_Explicit_ToPointer;
                }

                switch (t0Type)
                {
                    case SpecialType.System_Byte:
                    case SpecialType.System_SByte:
                    case SpecialType.System_Int16:
                    case SpecialType.System_UInt16:
                    case SpecialType.System_Char:
                    case SpecialType.System_UInt32:
                    case SpecialType.System_Int32:
                        return SpecialMember.System_IntPtr__op_Explicit_ToInt32;
                    case SpecialType.System_UInt64:
                    case SpecialType.System_Int64:
                    case SpecialType.System_Single:
                    case SpecialType.System_Double:
                    case SpecialType.System_Decimal:
                        return SpecialMember.System_IntPtr__op_Explicit_ToInt64;
                }
            }
            else if (s0Type == SpecialType.System_UIntPtr)
            {
                if (target.TypeKind == TypeKind.PointerType)
                {
                    return SpecialMember.System_UIntPtr__op_Explicit_ToPointer;
                }

                switch (t0Type)
                {
                    case SpecialType.System_SByte:
                    case SpecialType.System_Int16:
                    case SpecialType.System_Int32:
                    case SpecialType.System_Byte:
                    case SpecialType.System_UInt16:
                    case SpecialType.System_Char:
                    case SpecialType.System_UInt32:
                        return SpecialMember.System_UIntPtr__op_Explicit_ToUInt32;
                    case SpecialType.System_UInt64:
                    case SpecialType.System_Int64:
                    case SpecialType.System_Single:
                    case SpecialType.System_Double:
                    case SpecialType.System_Decimal:
                        return SpecialMember.System_UIntPtr__op_Explicit_ToUInt64;
                }
            }

            throw ExceptionUtilities.Unreachable;
        }
Example #15
0
        /// <summary>
        /// Does an expression of type <paramref name="expressionType"/> "match" a pattern that looks for
        /// type <paramref name="patternType"/>?
        /// 'true' if the matched type catches all of them, 'false' if it catches none of them, and
        /// 'null' if it might catch some of them. For this test we assume the expression's value
        /// isn't null.
        /// </summary>
        protected bool? ExpressionOfTypeMatchesPatternType(
            TypeSymbol expressionType,
            TypeSymbol patternType,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            if (expressionType == patternType)
            {
                return true;
            }

            var conversion = _conversions.ClassifyBuiltInConversion(expressionType, patternType, ref useSiteDiagnostics);

            // This is for classification purposes only; we discard use-site diagnostics. Use-site diagnostics will
            // be given if a conversion is actually used.
            switch (conversion.Kind)
            {
                case ConversionKind.Boxing:             // a value of type int matches a pattern of type object
                case ConversionKind.Identity:           // a value of a given type matches a pattern of that type
                case ConversionKind.ImplicitReference:  // a value of type string matches a pattern of type object
                    return true;

                case ConversionKind.ImplicitNullable:   // a value of type int matches a pattern of type int?
                case ConversionKind.ExplicitNullable:   // a non-null value of type "int?" matches a pattern of type int
                    // but if the types differ (e.g. one of them is type byte and the other is type int?).. no match
                    return ConversionsBase.HasIdentityConversion(expressionType.StrippedType().TupleUnderlyingTypeOrSelf(), patternType.StrippedType().TupleUnderlyingTypeOrSelf());

                case ConversionKind.ExplicitEnumeration:// a value of enum type does not match a pattern of integral type
                case ConversionKind.ExplicitNumeric:    // a value of type long does not match a pattern of type int
                case ConversionKind.ImplicitNumeric:    // a value of type short does not match a pattern of type int
                case ConversionKind.ImplicitTuple:      // distinct tuple types don't match
                case ConversionKind.NoConversion:
                    return false;

                case ConversionKind.ExplicitDynamic:    // a value of type dynamic might not match a pattern of type other than object
                case ConversionKind.ExplicitReference:  // a narrowing reference conversion might or might not succeed
                case ConversionKind.Unboxing:           // a value of type object might match a pattern of type int
                    return null;

                default:
                    // other conversions don't apply (e.g. conversions from expression, user-defined) and should not arise
                    throw ExceptionUtilities.UnexpectedValue(conversion.Kind);
            }
        }