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); }
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; }
/// <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); } }
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 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); }
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(); }
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; }
/// <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); } }