Exemplo n.º 1
0
        private BoundExpression MakeAsOperator(
            BoundAsOperator oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            BoundTypeExpression rewrittenTargetType,
            Conversion conversion,
            TypeSymbol rewrittenType)
        {
            // TODO: Handle dynamic operand type and target type
            Debug.Assert(rewrittenTargetType.Type.Equals(rewrittenType));

            // target type cannot be a non-nullable value type
            Debug.Assert(!rewrittenType.IsValueType || rewrittenType.IsNullableType());

            if (!_inExpressionLambda)
            {
                ConstantValue constantValue = Binder.GetAsOperatorConstantResult(rewrittenOperand.Type, rewrittenType, conversion.Kind, rewrittenOperand.ConstantValue);

                if (constantValue != null)
                {
                    Debug.Assert(constantValue.IsNull);
                    BoundExpression result = rewrittenType.IsNullableType() ? new BoundDefaultOperator(syntax, rewrittenType) : MakeLiteral(syntax, constantValue, rewrittenType);

                    if (rewrittenOperand.ConstantValue != null)
                    {
                        // No need to preserve any side-effects from the operand. 
                        // We also can keep the "constant" notion of the result, which
                        // enables some optimizations down the road.
                        return result;
                    }

                    return new BoundSequence(
                        syntax: syntax,
                        locals: ImmutableArray<LocalSymbol>.Empty,
                        sideEffects: ImmutableArray.Create<BoundExpression>(rewrittenOperand),
                        value: result,
                        type: rewrittenType);
                }

                if (conversion.IsImplicit)
                {
                    // Operand with bound implicit conversion to target type.
                    // We don't need a runtime check, generate a conversion for the operand instead.
                    return MakeConversionNode(syntax, rewrittenOperand, conversion, rewrittenType, @checked: false);
                }
            }

            return oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType);
        }
Exemplo n.º 2
0
        private BoundExpression VisitExpressionImpl(BoundExpression node)
        {
            ConstantValue constantValue = node.ConstantValue;

            if (constantValue != null)
            {
                TypeSymbol type = node.Type;
                if (type?.IsNullableType() != true)
                {
                    return(MakeLiteral(node.Syntax, constantValue, type));
                }
            }

            var visited = VisitExpressionWithStackGuard(node);

            // If you *really* need to change the type, consider using an indirect method
            // like compound assignment does (extra flag only passed when it is an expression
            // statement means that this constraint is not violated).
            // Dynamic type will be erased in emit phase. It is considered equivalent to Object in lowered bound trees.
            // Unused deconstructions are lowered to produce a return value that isn't a tuple type.
            Debug.Assert(visited == null || visited.HasErrors || ReferenceEquals(visited.Type, node.Type) ||
                         visited.Type.Equals(node.Type, TypeCompareKind.IgnoreDynamicAndTupleNames) ||
                         IsUnusedDeconstruction(node));

            return(visited);
        }
Exemplo n.º 3
0
        private TypeWithAnnotations(TypeSymbol defaultType, NullableAnnotation nullableAnnotation, Extensions extensions)
        {
            Debug.Assert(defaultType?.IsNullableType() != true || (nullableAnnotation != NullableAnnotation.Oblivious && nullableAnnotation != NullableAnnotation.NotAnnotated));
            Debug.Assert(extensions != null);

            _defaultType       = defaultType;
            NullableAnnotation = nullableAnnotation;
            _extensions        = extensions;
        }
Exemplo n.º 4
0
        private TypeWithAnnotations(TypeSymbol defaultType, NullableAnnotation nullableAnnotation, Extensions extensions)
        {
            Debug.Assert(defaultType?.IsNullableType() != true || nullableAnnotation == NullableAnnotation.Annotated);
            Debug.Assert(extensions != null);

            DefaultType        = defaultType;
            NullableAnnotation = nullableAnnotation;
            _extensions        = extensions;
        }
Exemplo n.º 5
0
 private TypeSymbolWithAnnotations(TypeSymbol defaultType, INonNullTypesContext nonNullTypesContext, bool isAnnotated, bool treatPossiblyNullableReferenceTypeTypeParameterAsNullable, Extensions extensions)
 {
     Debug.Assert((object)defaultType != null);
     Debug.Assert(!defaultType.IsNullableType() || isAnnotated);
     Debug.Assert(nonNullTypesContext != null);
     Debug.Assert(extensions != null);
     _defaultType = defaultType;
     IsAnnotated  = isAnnotated;
     _treatPossiblyNullableReferenceTypeTypeParameterAsNullable = treatPossiblyNullableReferenceTypeTypeParameterAsNullable;
     NonNullTypesContext = nonNullTypesContext;
     _extensions         = extensions;
 }
Exemplo n.º 6
0
        private BoundExpression ConvertToNullable(SyntaxNode syntax, TypeSymbol targetNullableType, BoundExpression underlyingValue)
        {
            Debug.Assert(targetNullableType.IsNullableType());
            Debug.Assert(TypeSymbol.Equals(targetNullableType.GetNullableUnderlyingType(), underlyingValue.Type, TypeCompareKind.AllIgnoreOptions));

            if (!TryGetNullableMethod(syntax, targetNullableType, SpecialMember.System_Nullable_T__ctor, out MethodSymbol nullableCtor))
            {
                return(BadExpression(syntax, targetNullableType, underlyingValue));
            }

            return(new BoundObjectCreationExpression(syntax, nullableCtor, underlyingValue));
        }
Exemplo n.º 7
0
        private static bool SatisfiesConstraintType(
            ConversionsBase conversions,
            TypeSymbol typeArgument,
            TypeSymbol constraintType,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            if (constraintType.IsErrorType())
            {
                return(false);
            }

            // Spec 4.4.4 describes the valid conversions from
            // type argument A to constraint type C:

            // "An identity conversion (6.1.1).
            // An implicit reference conversion (6.1.6). ..."
            if (conversions.HasIdentityOrImplicitReferenceConversion(typeArgument, constraintType, ref useSiteDiagnostics))
            {
                return(true);
            }

            // "... A boxing conversion (6.1.7), provided that type A is a non-nullable value type. ..."
            // NOTE: we extend this to allow, for example, a conversion from Nullable<T> to object.
            if (typeArgument.IsValueType &&
                conversions.HasBoxingConversion(typeArgument.IsNullableType() ? ((NamedTypeSymbol)typeArgument).ConstructedFrom : typeArgument, constraintType, ref useSiteDiagnostics))
            {
                return(true);
            }

            if (typeArgument.TypeKind == TypeKind.TypeParameter)
            {
                var typeParameter = (TypeParameterSymbol)typeArgument;

                // "... An implicit reference, boxing, or type parameter conversion
                // from type parameter A to C."
                if (conversions.HasImplicitTypeParameterConversion(typeParameter, constraintType, ref useSiteDiagnostics))
                {
                    return(true);
                }

                // TypeBind::SatisfiesBound allows cases where one of the
                // type parameter constraints satisfies the constraint.
                foreach (var typeArgumentConstraint in typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
                {
                    if (SatisfiesConstraintType(conversions, typeArgumentConstraint, constraintType, ref useSiteDiagnostics))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Exemplo n.º 8
0
        private BoundExpression ConvertCaseExpression(TypeSymbol switchGoverningType, CSharpSyntaxNode node, BoundExpression caseExpression, ref ConstantValue constantValueOpt, DiagnosticBag diagnostics, bool isGotoCaseExpr = false)
        {
            BoundExpression convertedCaseExpression;

            if (!isGotoCaseExpr)
            {
                // NOTE: This will allow user-defined conversions, even though they're not allowed here.  This is acceptable
                // because the result of a user-defined conversion does not have a ConstantValue and we'll report a diagnostic
                // to that effect below (same error code as Dev10).
                convertedCaseExpression = GenerateConversionForAssignment(switchGoverningType, caseExpression, diagnostics);
            }
            else
            {
                // SPEC VIOLATION for Dev10 COMPATIBILITY:

                // Dev10 compiler violates the SPEC comment below:
                //      "if the constant-expression is not implicitly convertible (§6.1) to
                //      the governing type of the nearest enclosing switch statement,
                //      a compile-time error occurs"

                // If there is no implicit conversion from gotoCaseExpression to switchGoverningType,
                // but there exists an explicit conversion, Dev10 compiler generates a warning "WRN_GotoCaseShouldConvert"
                // instead of an error. See test "CS0469_NoImplicitConversionWarning".

                // CONSIDER: Should we introduce a breaking change and violate Dev10 compatibility and follow the spec?

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                Conversion conversion = Conversions.ClassifyConversionFromExpression(caseExpression, switchGoverningType, ref useSiteDiagnostics);
                diagnostics.Add(node, useSiteDiagnostics);
                if (!conversion.IsValid)
                {
                    GenerateImplicitConversionError(diagnostics, node, conversion, caseExpression, switchGoverningType);
                }
                else if (!conversion.IsImplicit)
                {
                    diagnostics.Add(ErrorCode.WRN_GotoCaseShouldConvert, node.Location, switchGoverningType);
                }

                convertedCaseExpression = this.CreateConversion(caseExpression, conversion, switchGoverningType, diagnostics);
            }

            if (switchGoverningType.IsNullableType() && convertedCaseExpression.Kind == BoundKind.Conversion)
            {
                constantValueOpt = ((BoundConversion)convertedCaseExpression).Operand.ConstantValue;
            }
            else
            {
                constantValueOpt = convertedCaseExpression.ConstantValue;
            }

            return(convertedCaseExpression);
        }
Exemplo n.º 9
0
        public static bool IsNullableTypeOrTypeParameter(this TypeSymbol type)
        {
            if (type.TypeKind == TypeKind.TypeParameter)
            {
                var constraintTypes = ((TypeParameterSymbol)type).ConstraintTypesNoUseSiteDiagnostics;
                foreach (var constraintType in constraintTypes)
                {
                    if (IsNullableTypeOrTypeParameter(constraintType))
                    {
                        return(true);
                    }
                }
                return(false);
            }

            return(type.IsNullableType());
        }
        private void GetUserDefinedUnaryOperatorsFromType(
            TypeSymbol constrainedToTypeOpt,
            NamedTypeSymbol type,
            UnaryOperatorKind kind,
            string name,
            ArrayBuilder <UnaryOperatorSignature> operators)
        {
            foreach (MethodSymbol op in type.GetOperators(name))
            {
                // If we're in error recovery, we might have bad operators. Just ignore it.
                if (op.ParameterCount != 1 || op.ReturnsVoid)
                {
                    continue;
                }

                TypeSymbol operandType = op.GetParameterType(0);
                TypeSymbol resultType  = op.ReturnType;

                operators.Add(new UnaryOperatorSignature(UnaryOperatorKind.UserDefined | kind, operandType, resultType, op, constrainedToTypeOpt));

                // SPEC: For the unary operators + ++ - -- ! ~ a lifted form of an operator exists
                // SPEC: if the operand and its result types are both non-nullable value types.
                // SPEC: The lifted form is constructed by adding a single ? modifier to the
                // SPEC: operator and result types.
                switch (kind)
                {
                case UnaryOperatorKind.UnaryPlus:
                case UnaryOperatorKind.PrefixDecrement:
                case UnaryOperatorKind.PrefixIncrement:
                case UnaryOperatorKind.UnaryMinus:
                case UnaryOperatorKind.PostfixDecrement:
                case UnaryOperatorKind.PostfixIncrement:
                case UnaryOperatorKind.LogicalNegation:
                case UnaryOperatorKind.BitwiseComplement:
                    if (operandType.IsValueType && !operandType.IsNullableType() &&
                        resultType.IsValueType && !resultType.IsNullableType())
                    {
                        operators.Add(new UnaryOperatorSignature(
                                          UnaryOperatorKind.Lifted | UnaryOperatorKind.UserDefined | kind,
                                          MakeNullable(operandType), MakeNullable(resultType), op, constrainedToTypeOpt));
                    }
                    break;
                }
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Returns true if the type is a valid switch expression type.
        /// </summary>
        internal static bool IsValidSwitchGoverningType(this TypeSymbol type, bool isTargetTypeOfUserDefinedOp = false)
        {
            // SPEC:    The governing type of a switch statement is established by the switch expression.
            // SPEC:    1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint,
            // SPEC:       long, ulong, bool, char, string, or an enum-type, or if it is the nullable type
            // SPEC:       corresponding to one of these types, then that is the governing type of the switch statement.
            // SPEC:    2) Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the
            // SPEC:       type of the switch expression to one of the following possible governing types:
            // SPEC:       sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type
            // SPEC:       corresponding to one of those types

            Debug.Assert((object)type != null);
            if (type.IsNullableType())
            {
                type = type.GetNullableUnderlyingType();
            }

            // User-defined implicit conversion with target type as Enum type is not valid.
            if (!isTargetTypeOfUserDefinedOp && type.IsEnumType())
            {
                type = type.GetEnumUnderlyingType();
            }

            switch (type.SpecialType)
            {
            case SpecialType.System_SByte:
            case SpecialType.System_Byte:
            case SpecialType.System_Int16:
            case SpecialType.System_UInt16:
            case SpecialType.System_Int32:
            case SpecialType.System_UInt32:
            case SpecialType.System_Int64:
            case SpecialType.System_UInt64:
            case SpecialType.System_Char:
            case SpecialType.System_String:
                return(true);

            case SpecialType.System_Boolean:
                // User-defined implicit conversion with target type as bool type is not valid.
                return(!isTargetTypeOfUserDefinedOp);
            }

            return(false);
        }
Exemplo n.º 12
0
        internal static TypeWithAnnotations Create(TypeSymbol typeSymbol, NullableAnnotation nullableAnnotation = NullableAnnotation.Oblivious, ImmutableArray<CustomModifier> customModifiers = default)
        {
            if (typeSymbol is null && nullableAnnotation == 0)
            {
                return default;
            }

            switch (nullableAnnotation)
            {
                case NullableAnnotation.Oblivious:
                case NullableAnnotation.NotAnnotated:
                    if (typeSymbol?.IsNullableType() == true)
                    {
                        // int?, T? where T : struct (add annotation)
                        nullableAnnotation = NullableAnnotation.Annotated;
                    }
                    break;
            }

            return CreateNonLazyType(typeSymbol, nullableAnnotation, customModifiers.NullToEmpty());
        }
Exemplo n.º 13
0
        private static ConversionKind ClassifyNullLiteralConversion(BoundExpression source, TypeSymbol destination)
        {
            Debug.Assert((object)source != null);
            Debug.Assert((object)destination != null);

            if (!source.IsLiteralNull())
            {
                return(ConversionKind.NoConversion);
            }

            // SPEC: An implicit conversion exists from the null literal to any nullable type.
            if (destination.IsNullableType())
            {
                // The spec defines a "null literal conversion" specifically as a conversion from
                // null to nullable type.
                return(ConversionKind.NullLiteral);
            }

            // SPEC: An implicit conversion exists from the null literal to any reference type.
            // SPEC: An implicit conversion exists from the null literal to type parameter T,
            // SPEC: provided T is known to be a reference type. [...] The conversion [is] classified
            // SPEC: as implicit reference conversion.

            if (destination.IsReferenceType)
            {
                return(ConversionKind.ImplicitReference);
            }

            // SPEC: The set of implicit conversions is extended to include...
            // SPEC: ... from the null literal to any pointer type.

            if (destination is PointerTypeSymbol)
            {
                return(ConversionKind.NullToPointer);
            }

            return(ConversionKind.NoConversion);
        }
        private BoundExpression MakeAsOperator(
            BoundAsOperator oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            BoundTypeExpression rewrittenTargetType,
            Conversion conversion,
            TypeSymbol rewrittenType)
        {
            // TODO: Handle dynamic operand type and target type
            Debug.Assert(rewrittenTargetType.Type.Equals(rewrittenType));

            // target type cannot be a non-nullable value type
            Debug.Assert(!rewrittenType.IsValueType || rewrittenType.IsNullableType());

            if (!inExpressionLambda)
            {
                ConstantValue constantValue = Binder.GetAsOperatorConstantResult(rewrittenOperand.Type, rewrittenType, conversion.Kind, rewrittenOperand.ConstantValue);
                Debug.Assert(constantValue == null || constantValue.IsNull);

                if (conversion.IsImplicit)
                {
                    // Operand with bound implicit conversion to target type.
                    // We don't need a runtime check, generate a conversion for the operand instead.
                    return(MakeConversion(syntax, rewrittenOperand, conversion, rewrittenType, @checked: false, constantValueOpt: constantValue));
                }
                else if (constantValue != null)
                {
                    return(new BoundSequence(
                               syntax: syntax,
                               locals: ImmutableArray <LocalSymbol> .Empty,
                               sideEffects: ImmutableArray.Create <BoundExpression>(rewrittenOperand),
                               value: MakeLiteral(syntax, constantValue, rewrittenType),
                               type: rewrittenType));
                }
            }

            return(oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType));
        }
Exemplo n.º 15
0
        private BoundExpression MakeAsOperator(
            BoundAsOperator oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            BoundTypeExpression rewrittenTargetType,
            Conversion conversion,
            TypeSymbol rewrittenType)
        {
            // TODO: Handle dynamic operand type and target type
            Debug.Assert(rewrittenTargetType.Type.Equals(rewrittenType));

            // target type cannot be a non-nullable value type
            Debug.Assert(!rewrittenType.IsValueType || rewrittenType.IsNullableType());

            if (!inExpressionLambda)
            {
                ConstantValue constantValue = Binder.GetAsOperatorConstantResult(rewrittenOperand.Type, rewrittenType, conversion.Kind, rewrittenOperand.ConstantValue);
                Debug.Assert(constantValue == null || constantValue.IsNull);

                if (conversion.IsImplicit)
                {
                    // Operand with bound implicit conversion to target type.
                    // We don't need a runtime check, generate a conversion for the operand instead.
                    return MakeConversion(syntax, rewrittenOperand, conversion, rewrittenType, @checked: false, constantValueOpt: constantValue);
                }
                else if (constantValue != null)
                {
                    return new BoundSequence(
                        syntax: syntax,
                        locals: ImmutableArray<LocalSymbol>.Empty,
                        sideEffects: ImmutableArray.Create<BoundExpression>(rewrittenOperand),
                        value: MakeLiteral(syntax, constantValue, rewrittenType),
                        type: rewrittenType);
                }
            }

            return oldNode.Update(rewrittenOperand, rewrittenTargetType, conversion, rewrittenType);
        }
Exemplo n.º 16
0
        private BoundExpression VisitExpressionImpl(BoundExpression node)
        {
            ConstantValue constantValue = node.ConstantValue;

            if (constantValue != null)
            {
                TypeSymbol type = node.Type;
                if (((object)type == null || !type.IsNullableType()))
                {
                    return(MakeLiteral(node.Syntax, constantValue, type));
                }
            }

            var visited = (BoundExpression)base.Visit(node);

            // If you *really* need to change the type, consider using an indirect method
            // like compound assignment does (extra flag only passed when it is an expression
            // statement means that this constraint is not violated).
            // Dynamic type will be erased in emit phase. It is considered equivalent to Object in lowered bound trees.
            Debug.Assert(visited == null || visited.HasErrors || ReferenceEquals(visited.Type, node.Type) || visited.Type.Equals(node.Type, ignoreDynamic: true));

            return(visited);
        }
Exemplo n.º 17
0
        internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSyntaxNode node, BoundExpression expression, ref ConstantValue constantValue, DiagnosticBag diagnostics)
        {
            // NOTE: This will allow user-defined conversions, even though they're not allowed here.  This is acceptable
            // because the result of a user-defined conversion does not have a ConstantValue and we'll report a diagnostic
            // to that effect later.
            BoundExpression convertedExpression = GenerateConversionForAssignment(inputType, expression, diagnostics);

            if (convertedExpression.Kind == BoundKind.Conversion)
            {
                var conversion = (BoundConversion)convertedExpression;
                var operand    = conversion.Operand;
                if (inputType.IsNullableType() && (convertedExpression.ConstantValue == null || !convertedExpression.ConstantValue.IsNull))
                {
                    // Null is a special case here because we want to compare null to the Nullable<T> itself, not to the underlying type.
                    var discardedDiagnostics = DiagnosticBag.GetInstance(); // We are not intested in the diagnostic that get created here
                    convertedExpression = CreateConversion(operand, inputType.GetNullableUnderlyingType(), discardedDiagnostics);
                    discardedDiagnostics.Free();
                }
                else if ((conversion.ConversionKind == ConversionKind.Boxing || conversion.ConversionKind == ConversionKind.ImplicitReference) &&
                         operand.ConstantValue != null && convertedExpression.ConstantValue == null)
                {
                    // A boxed constant (or string converted to object) is a special case because we prefer
                    // to compare to the pre-converted value by casting the input value to the type of the constant
                    // (that is, unboxing or downcasting it) and then testing the resulting value using primitives.
                    // That is much more efficient than calling object.Equals(x, y), and we can share the downcasted
                    // input value among many constant tests.
                    convertedExpression = operand;
                }
                else if (conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true)
                {
                    convertedExpression = operand;
                }
            }

            constantValue = convertedExpression.ConstantValue;
            return(convertedExpression);
        }
Exemplo n.º 18
0
        public TypeSymbol EmitConvertToPhpValue(TypeSymbol from, TypeRefMask fromHint)
        {
            // Nullable<T> -> HasValue ? T : NULL
            if (from.IsNullableType())
            {
                from = EmitNullableCastToNull(from, false); // (HasValue ? Value : NULL)
            }

            var conv = DeclaringCompilation.ClassifyCommonConversion(from, CoreTypes.PhpValue.Symbol);

            if (conv.IsImplicit)
            {
                this.EmitConversion(conv, from, CoreTypes.PhpValue.Symbol);
            }
            else
            {
                // some conversion we did not implement as operator yet:

                if (from.IsReferenceType)
                {
                    EmitCall(ILOpCode.Call, CoreMethods.PhpValue.FromClass_Object).Expect(CoreTypes.PhpValue);
                }
                else if (from.SpecialType == SpecialType.System_Void)
                {
                    // PhpValue.Void
                    Emit_PhpValue_Void();
                }
                else
                {
                    // box & wrap to PhpValue.Object
                    // Template: PhpValue.FromStruct<T>( STACK )
                    EmitCall(ILOpCode.Call, CoreMethods.PhpValue.FromStruct_T.Symbol.Construct(from)).Expect(CoreTypes.PhpValue);
                }
            }
            //
            return(CoreTypes.PhpValue);
        }
Exemplo n.º 19
0
        public TypeSymbol EmitConvertToPhpValue(TypeSymbol from, TypeRefMask fromHint)
        {
            // Nullable<T> -> HasValue ? T : NULL
            if (from.IsNullableType())
            {
                from = EmitNullableCastToNull(from, false); // (HasValue ? Value : NULL)
            }

            var conv = DeclaringCompilation.ClassifyCommonConversion(from, CoreTypes.PhpValue.Symbol);

            if (conv.IsImplicit)
            {
                this.EmitConversion(conv, from, CoreTypes.PhpValue.Symbol);
            }
            else
            {
                // some conversion we did not implement as operator yet:

                if (from.IsReferenceType)
                {
                    EmitCall(ILOpCode.Call, CoreMethods.PhpValue.FromClass_Object)
                    .Expect(CoreTypes.PhpValue);
                }
                else if (from.SpecialType == SpecialType.System_Void)
                {
                    // PhpValue.Void
                    Emit_PhpValue_Void();
                }
                else
                {
                    throw ExceptionUtilities.NotImplementedException(this, $"{from.Name} -> PhpValue");
                }
            }
            //
            return(CoreTypes.PhpValue);
        }
Exemplo n.º 20
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);
            }
        }
        private void AddUserDefinedConversionsToExplicitCandidateSet(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ArrayBuilder<UserDefinedConversionAnalysis> u,
            NamedTypeSymbol declaringType,
            string operatorName,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);
            Debug.Assert(u != null);
            Debug.Assert((object)declaringType != null);
            Debug.Assert(operatorName != null);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U.
            // SPEC: The set consists of the user-defined and lifted implicit or explicit 
            // SPEC: conversion operators declared by the classes and structs in D that convert 
            // SPEC: from a type encompassing E or encompassed by S (if it exists) to a type
            // SPEC: encompassing or encompassed by T. 

            // DELIBERATE SPEC VIOLATION:
            //
            // The spec here essentially says that we add an applicable "regular" conversion and 
            // an applicable lifted conversion, if there is one, to the candidate set, and then
            // let them duke it out to determine which one is "best".
            //
            // This is not at all what the native compiler does, and attempting to implement
            // the specification, or slight variations on it, produces too many backwards-compatibility
            // breaking changes.
            //
            // The native compiler deviates from the specification in two major ways here.
            // First, it does not add *both* the regular and lifted forms to the candidate set.
            // Second, the way it characterizes a "lifted" form is very, very different from
            // how the specification characterizes a lifted form. 
            //
            // An operation, in this case, X-->Y, is properly said to be "lifted" to X?-->Y? via
            // the rule that X?-->Y? matches the behavior of X-->Y for non-null X, and converts
            // null X to null Y otherwise.
            //
            // The native compiler, by contrast, takes the existing operator and "lifts" either
            // the operator's parameter type or the operator's return type to nullable. For
            // example, a conversion from X?-->Y would be "lifted" to X?-->Y? by making the
            // conversion from X? to Y, and then from Y to Y?.  No "lifting" semantics
            // are imposed; we do not check to see if the X? is null. This operator is not
            // actually "lifted" at all; rather, an implicit conversion is applied to the 
            // output. **The native compiler considers the result type Y? of that standard implicit
            // conversion to be the result type of the "lifted" conversion**, rather than
            // properly considering Y to be the result type of the conversion for the purposes 
            // of computing the best output type.
            //
            // Moreover: the native compiler actually *does* implement nullable lifting semantics
            // in the case where the input type of the user-defined conversion is a non-nullable
            // value type and the output type is a nullable value type **or pointer type, or 
            // reference type**. This is an enormous departure from the specification; the
            // native compiler will take a user-defined conversion from X-->Y? or X-->C and "lift"
            // it to a conversion from X?-->Y? or X?-->C that has nullable semantics.
            // 
            // This is quite confusing. In this code we will classify the conversion as either
            // "normal" or "lifted" on the basis of *whether or not special lifting semantics
            // are to be applied*. That is, whether or not a later rewriting pass is going to
            // need to insert a check to see if the source expression is null, and decide
            // whether or not to call the underlying unlifted conversion or produce a null
            // value without calling the unlifted conversion.
            // DELIBERATE SPEC VIOLATION: See the comment regarding bug 17021 in 
            // UserDefinedImplicitConversions.cs.

            if ((object)source != null && source.IsInterfaceType() || target.IsInterfaceType())
            {
                return;
            }

            foreach (MethodSymbol op in declaringType.GetOperators(operatorName))
            {
                // We might have a bad operator and be in an error recovery situation. Ignore it.
                if (op.ReturnsVoid || op.ParameterCount != 1 || op.ReturnType.TypeKind == TypeKind.Error)
                {
                    continue;
                }

                TypeSymbol convertsFrom = op.ParameterTypes[0];
                TypeSymbol convertsTo = op.ReturnType;
                Conversion fromConversion = EncompassingExplicitConversion(sourceExpression, source, convertsFrom, ref useSiteDiagnostics);
                Conversion toConversion = EncompassingExplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);

                // We accept candidates for which the parameter type encompasses the *underlying* source type.
                if (!fromConversion.Exists &&
                    (object)source != null &&
                    source.IsNullableType() &&
                    EncompassingExplicitConversion(null, source.GetNullableUnderlyingType(), convertsFrom, ref useSiteDiagnostics).Exists)
                {
                    fromConversion = ClassifyConversion(source, convertsFrom, ref useSiteDiagnostics, builtinOnly: true);
                }

                // As in dev11 (and the revised spec), we also accept candidates for which the return type is encompassed by the *stripped* target type.
                if (!toConversion.Exists &&
                    (object)target != null &&
                    target.IsNullableType() &&
                    EncompassingExplicitConversion(null, convertsTo, target.GetNullableUnderlyingType(), ref useSiteDiagnostics).Exists)
                {
                    toConversion = ClassifyConversion(convertsTo, target, ref useSiteDiagnostics, builtinOnly: true);
                }

                // In the corresponding implicit conversion code we can get away with first 
                // checking to see if standard implicit conversions exist from the source type
                // to the parameter type, and from the return type to the target type. If not,
                // then we can check for a lifted operator.
                //
                // That's not going to cut it in the explicit conversion code. Suppose we have
                // a conversion X-->Y and have source type X? and target type Y?. There *are*
                // standard explicit conversions from X?-->X and Y?-->Y, but we do not want
                // to bind this as an *unlifted* conversion from X? to Y?; we want such a thing
                // to be a *lifted* conversion from X? to Y?, that checks for null on the source
                // and decides to not call the underlying user-defined conversion if it is null.
                //
                // We therefore cannot do what we do in the implicit conversions, where we check
                // to see if the unlifted conversion works, and if it does, then don't add the lifted
                // conversion at all. Rather, we have to see if what we're building here is a 
                // lifted conversion or not.
                //
                // Under what circumstances is this conversion a lifted conversion? (In the 
                // "spec" sense of a lifted conversion; that is, that we check for null
                // and skip the user-defined conversion if necessary).
                //
                // * The source type must be a nullable value type.
                // * The parameter type must be a non-nullable value type.
                // * The target type must be able to take on a null value.

                if (fromConversion.Exists && toConversion.Exists)
                {
                    if ((object)source != null && source.IsNullableType() && convertsFrom.IsNonNullableValueType() && target.CanBeAssignedNull())
                    {
                        TypeSymbol nullableFrom = MakeNullableType(convertsFrom);
                        TypeSymbol nullableTo = convertsTo.IsNonNullableValueType() ? MakeNullableType(convertsTo) : convertsTo;
                        Conversion liftedFromConversion = EncompassingExplicitConversion(sourceExpression, source, nullableFrom, ref useSiteDiagnostics);
                        Conversion liftedToConversion = EncompassingExplicitConversion(null, nullableTo, target, ref useSiteDiagnostics);
                        Debug.Assert(liftedFromConversion.Exists);
                        Debug.Assert(liftedToConversion.Exists);
                        u.Add(UserDefinedConversionAnalysis.Lifted(op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo));
                    }
                    else
                    {

                        // There is an additional spec violation in the native compiler. Suppose
                        // we have a conversion from X-->Y and are asked to do "Y? y = new X();"  Clearly
                        // the intention is to convert from X-->Y via the implicit conversion, and then
                        // stick a standard implicit conversion from Y-->Y? on the back end. **In this 
                        // situation, the native compiler treats the conversion as though it were
                        // actually X-->Y? in source for the purposes of determining the best target
                        // type of a set of operators.
                        //
                        // Similarly, if we have a conversion from X-->Y and are asked to do 
                        // an explicit conversion from X? to Y then we treat the conversion as
                        // though it really were X?-->Y for the purposes of determining the best
                        // source type of a set of operators.
                        //
                        // We perpetuate these fictions here.

                        if (target.IsNullableType() && convertsTo.IsNonNullableValueType())
                        {
                            convertsTo = MakeNullableType(convertsTo);
                            toConversion = EncompassingExplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);
                        }

                        if ((object)source != null && source.IsNullableType() && convertsFrom.IsNonNullableValueType())
                        {
                            convertsFrom = MakeNullableType(convertsFrom);
                            fromConversion = EncompassingExplicitConversion(null, convertsFrom, source, ref useSiteDiagnostics);
                        }

                        u.Add(UserDefinedConversionAnalysis.Normal(op, fromConversion, toConversion, convertsFrom, convertsTo));
                    }
                }
            }
        }
Exemplo n.º 22
0
            private static int TypeToIndex(TypeSymbol type)
            {
                switch (type.GetSpecialTypeSafe())
                {
                case SpecialType.System_Object: return(0);

                case SpecialType.System_String: return(1);

                case SpecialType.System_Boolean: return(2);

                case SpecialType.System_Char: return(3);

                case SpecialType.System_SByte: return(4);

                case SpecialType.System_Int16: return(5);

                case SpecialType.System_Int32: return(6);

                case SpecialType.System_Int64: return(7);

                case SpecialType.System_Byte: return(8);

                case SpecialType.System_UInt16: return(9);

                case SpecialType.System_UInt32: return(10);

                case SpecialType.System_UInt64: return(11);

                case SpecialType.System_Single: return(12);

                case SpecialType.System_Double: return(13);

                case SpecialType.System_Decimal: return(14);

                case SpecialType.None:
                    if ((object)type != null && type.IsNullableType())
                    {
                        TypeSymbol underlyingType = type.GetNullableUnderlyingType();

                        switch (underlyingType.GetSpecialTypeSafe())
                        {
                        case SpecialType.System_Boolean: return(15);

                        case SpecialType.System_Char: return(16);

                        case SpecialType.System_SByte: return(17);

                        case SpecialType.System_Int16: return(18);

                        case SpecialType.System_Int32: return(19);

                        case SpecialType.System_Int64: return(20);

                        case SpecialType.System_Byte: return(21);

                        case SpecialType.System_UInt16: return(22);

                        case SpecialType.System_UInt32: return(23);

                        case SpecialType.System_UInt64: return(24);

                        case SpecialType.System_Single: return(25);

                        case SpecialType.System_Double: return(26);

                        case SpecialType.System_Decimal: return(27);
                        }
                    }

                    // fall through
                    goto default;

                default:
                    return(-1);
                }
            }
Exemplo n.º 23
0
        private BoundExpression CreateTupleLiteralConversion(CSharpSyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
        {
            // We have a successful tuple conversion; rather than producing a separate conversion node 
            // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments.

            Debug.Assert(conversion.Kind == ConversionKind.ImplicitTupleLiteral || conversion.Kind == ConversionKind.ImplicitNullable);
            Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType());

            TypeSymbol destinationWithoutNullable = conversion.Kind == ConversionKind.ImplicitNullable ?
                                                                           destinationWithoutNullable = destination.GetNullableUnderlyingType() :
                                                                           destination;

            NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable;

            if (targetType.IsTupleType)
            {
                var destTupleType = (TupleTypeSymbol)targetType;
                // do not lose the original element names in the literal if different from names in the target

                // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013)
                targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt);
            }

            var arguments = sourceTuple.Arguments;
            var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length);

            ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible();
            Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?");

            for (int i = 0; i < arguments.Length; i++)
            {
                var argument = arguments[i];
                var destType = targetElementTypes[i];

                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                Conversion elementConversion;
                if (isCast)
                {
                    elementConversion = this.Conversions.ClassifyConversionForCast(argument, destType, ref useSiteDiagnostics);
                }
                else
                {
                    elementConversion = this.Conversions.ClassifyConversionFromExpression(argument, destType, ref useSiteDiagnostics);
                }

                diagnostics.Add(syntax, useSiteDiagnostics);
                convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics));
            }

            BoundExpression result = new BoundConvertedTupleLiteral(
                sourceTuple.Syntax,
                sourceTuple.Type,
                convertedArguments.ToImmutableAndFree(), 
                targetType);

            // We need to preserve any conversion that changes the type (even identity conversions),
            // or that was explicitly written in code (so that GetSemanticInfo can find the syntax in the bound tree).
            if (!isCast && targetType == destination)
            {
                return result;
            }

            // if we have a nullable cast combined with a name/dynamic cast
            // name/dynamic cast must happen before converting to nullable
            if (conversion.Kind == ConversionKind.ImplicitNullable &&
                destinationWithoutNullable != targetType)
            {
                Debug.Assert(destinationWithoutNullable.Equals(targetType, ignoreDynamic: true));

                result = new BoundConversion(
                    syntax,
                    result,
                    Conversion.Identity,
                    @checked: false,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: destinationWithoutNullable)
                { WasCompilerGenerated = sourceTuple.WasCompilerGenerated };
            }
                                  
            return new BoundConversion(
                syntax,
                result,
                conversion,
                @checked: false,
                explicitCastInCode: isCast,
                constantValueOpt: ConstantValue.NotAvailable,
                type: destination)
            { WasCompilerGenerated = sourceTuple.WasCompilerGenerated };
        }
Exemplo n.º 24
0
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
            BoundExpression operand,
            TypeSymbol operandType,
            TypeSymbol patternType,
            bool patternTypeWasInSource,
            bool isVar,
            DiagnosticBag diagnostics)
        {
            if (operandType?.IsErrorType() == true || patternType?.IsErrorType() == true)
            {
                return false;
            }
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return true;
            }
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return true;
            }
            else if (operand != null && operandType == (object)null && !operand.HasAnyErrors)
            {
                // It is an error to use pattern-matching with a null, method group, or lambda
                Error(diagnostics, ErrorCode.ERR_BadIsPatternExpression, operand.Syntax);
                return true;
            }
            else if (!isVar)
            {
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                Conversion conversion =
                    operand != null
                    ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast: true)
                    : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true);
                diagnostics.Add(typeSyntax, useSiteDiagnostics);
                switch (conversion.Kind)
                {
                    case ConversionKind.Boxing:
                    case ConversionKind.ExplicitNullable:
                    case ConversionKind.ExplicitReference:
                    case ConversionKind.Identity:
                    case ConversionKind.ImplicitReference:
                    case ConversionKind.Unboxing:
                    case ConversionKind.NullLiteral:
                    case ConversionKind.ImplicitNullable:
                        // these are the conversions allowed by a pattern match
                        break;
                    //case ConversionKind.ExplicitNumeric:  // we do not perform numeric conversions of the operand
                    //case ConversionKind.ImplicitConstant:
                    //case ConversionKind.ImplicitNumeric:
                    default:
                        Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
                        return true;
                }
            }

            return false;
        }
Exemplo n.º 25
0
        /// <summary>
        /// This method find the set of applicable user-defined and lifted conversion operators, u.
        /// The set consists of the user-defined and lifted implicit conversion operators declared by
        /// the classes and structs in d that convert from a type encompassing source to a type encompassed by target.
        /// However if allowAnyTarget is true, then it considers all operators that convert from a type encompassing source
        /// to any target. This flag must be set only if we are computing user defined conversions from a given source
        /// type to any target type.
        /// </summary>
        /// <remarks>
        /// Currently allowAnyTarget flag is only set to true by <see cref="AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType"/>,
        /// where we must consider user defined implicit conversions from the type of the switch expression to
        /// any of the possible switch governing types.
        /// </remarks>
        private void ComputeApplicableUserDefinedImplicitConversionSet(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ArrayBuilder <NamedTypeSymbol> d,
            ArrayBuilder <UserDefinedConversionAnalysis> u,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics,
            bool allowAnyTarget = false)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert(((object)target != null) == !allowAnyTarget);
            Debug.Assert(d != null);
            Debug.Assert(u != null);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U.
            // SPEC: The set consists of the user-defined and lifted implicit conversion operators
            // SPEC: declared by the classes and structs in D that convert from a type encompassing
            // SPEC: E to a type encompassed by T. If U is empty, the conversion is undefined and
            // SPEC: a compile-time error occurs.

            // SPEC: Give a user-defined conversion operator that converts from a non-nullable
            // SPEC: value type S to a non-nullable value type T, a lifted conversion operator
            // SPEC: exists that converts from S? to T?.

            // DELIBERATE SPEC VIOLATION:
            //
            // The spec here essentially says that we add an applicable "regular" conversion and
            // an applicable lifted conversion, if there is one, to the candidate set, and then
            // let them duke it out to determine which one is "best".
            //
            // This is not at all what the native compiler does, and attempting to implement
            // the specification, or slight variations on it, produces too many backwards-compatibility
            // breaking changes.
            //
            // The native compiler deviates from the specification in two major ways here.
            // First, it does not add *both* the regular and lifted forms to the candidate set.
            // Second, the way it characterizes a "lifted" form is very, very different from
            // how the specification characterizes a lifted form.
            //
            // An operation, in this case, X-->Y, is properly said to be "lifted" to X?-->Y? via
            // the rule that X?-->Y? matches the behavior of X-->Y for non-null X, and converts
            // null X to null Y otherwise.
            //
            // The native compiler, by contrast, takes the existing operator and "lifts" either
            // the operator's parameter type or the operator's return type to nullable. For
            // example, a conversion from X?-->Y would be "lifted" to X?-->Y? by making the
            // conversion from X? to Y, and then from Y to Y?.  No "lifting" semantics
            // are imposed; we do not check to see if the X? is null. This operator is not
            // actually "lifted" at all; rather, an implicit conversion is applied to the
            // output. **The native compiler considers the result type Y? of that standard implicit
            // conversion to be the result type of the "lifted" conversion**, rather than
            // properly considering Y to be the result type of the conversion for the purposes
            // of computing the best output type.
            //
            // MOREOVER: the native compiler actually *does* implement nullable lifting semantics
            // in the case where the input type of the user-defined conversion is a non-nullable
            // value type and the output type is a nullable value type **or pointer type, or
            // reference type**. This is an enormous departure from the specification; the
            // native compiler will take a user-defined conversion from X-->Y? or X-->C and "lift"
            // it to a conversion from X?-->Y? or X?-->C that has nullable semantics.
            //
            // This is quite confusing. In this code we will classify the conversion as either
            // "normal" or "lifted" on the basis of *whether or not special lifting semantics
            // are to be applied*. That is, whether or not a later rewriting pass is going to
            // need to insert a check to see if the source expression is null, and decide
            // whether or not to call the underlying unlifted conversion or produce a null
            // value without calling the unlifted conversion.

            // DELIBERATE SPEC VIOLATION (See bug 17021)
            // The specification defines a type U as "encompassing" a type V
            // if there is a standard implicit conversion from U to V, and
            // neither are interface types.
            //
            // The intention of this language is to ensure that we do not allow user-defined
            // conversions that involve interfaces. We have a reasonable expectation that a
            // conversion that involves an interface is one that preserves referential identity,
            // and user-defined conversions usually do not.
            //
            // Now, suppose we have a standard conversion from Alpha to Beta, a user-defined
            // conversion from Beta to Gamma, and a standard conversion from Gamma to Delta.
            // The specification allows the implicit conversion from Alpha to Delta only if
            // Beta encompasses Alpha and Delta encompasses Gamma.  And therefore, none of them
            // can be interface types, de jure.
            //
            // However, the dev10 compiler only checks Alpha and Delta to see if they are interfaces,
            // and allows Beta and Gamma to be interfaces.
            //
            // So what's the big deal there? It's not legal to define a user-defined conversion where
            // the input or output types are interfaces, right?
            //
            // It is not legal to define such a conversion, no, but it is legal to create one via generic
            // construction. If we have a conversion from T to C<T>, then C<I> has a conversion from I to C<I>.
            //
            // The dev10 compiler fails to check for this situation. This means that,
            // you can convert from int to C<IComparable> because int implements IComparable, but cannot
            // convert from IComparable to C<IComparable>!
            //
            // Unfortunately, we know of several real programs that rely upon this bug, so we are going
            // to reproduce it here.

            if ((object)source != null && source.IsInterfaceType() || (object)target != null && target.IsInterfaceType())
            {
                return;
            }

            foreach (NamedTypeSymbol declaringType in d)
            {
                foreach (MethodSymbol op in declaringType.GetOperators(WellKnownMemberNames.ImplicitConversionName))
                {
                    // We might have a bad operator and be in an error recovery situation. Ignore it.
                    if (op.ReturnsVoid || op.ParameterCount != 1)
                    {
                        continue;
                    }

                    TypeSymbol convertsFrom   = op.ParameterTypes[0];
                    TypeSymbol convertsTo     = op.ReturnType;
                    Conversion fromConversion = EncompassingImplicitConversion(sourceExpression, source, convertsFrom, ref useSiteDiagnostics);
                    Conversion toConversion   = allowAnyTarget ? Conversion.Identity :
                                                EncompassingImplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);

                    if (fromConversion.Exists && toConversion.Exists)
                    {
                        // There is an additional spec violation in the native compiler. Suppose
                        // we have a conversion from X-->Y and are asked to do "Y? y = new X();"  Clearly
                        // the intention is to convert from X-->Y via the implicit conversion, and then
                        // stick a standard implicit conversion from Y-->Y? on the back end. **In this
                        // situation, the native compiler treats the conversion as though it were
                        // actually X-->Y? in source for the purposes of determining the best target
                        // type of an operator.
                        //
                        // We perpetuate this fiction here.

                        if ((object)target != null && target.IsNullableType() && convertsTo.IsNonNullableValueType())
                        {
                            convertsTo   = MakeNullableType(convertsTo);
                            toConversion = allowAnyTarget ? Conversion.Identity :
                                           EncompassingImplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);
                        }

                        u.Add(UserDefinedConversionAnalysis.Normal(op, fromConversion, toConversion, convertsFrom, convertsTo));
                    }
                    else if ((object)source != null && source.IsNullableType() && convertsFrom.IsNonNullableValueType() &&
                             (allowAnyTarget || target.CanBeAssignedNull()))
                    {
                        // As mentioned above, here we diverge from the specification, in two ways.
                        // First, we only check for the lifted form if the normal form was inapplicable.
                        // Second, we are supposed to apply lifting semantics only if the conversion
                        // parameter and return types are *both* non-nullable value types.
                        //
                        // In fact the native compiler determines whether to check for a lifted form on
                        // the basis of:
                        //
                        // * Is the type we are ultimately converting from a nullable value type?
                        // * Is the parameter type of the conversion a non-nullable value type?
                        // * Is the type we are ultimately converting to a nullable value type,
                        //   pointer type, or reference type?
                        //
                        // If the answer to all those questions is "yes" then we lift to nullable
                        // and see if the resulting operator is applicable.
                        TypeSymbol nullableFrom         = MakeNullableType(convertsFrom);
                        TypeSymbol nullableTo           = convertsTo.IsNonNullableValueType() ? MakeNullableType(convertsTo) : convertsTo;
                        Conversion liftedFromConversion = EncompassingImplicitConversion(sourceExpression, source, nullableFrom, ref useSiteDiagnostics);
                        Conversion liftedToConversion   = !allowAnyTarget?
                                                          EncompassingImplicitConversion(null, nullableTo, target, ref useSiteDiagnostics) :
                                                              Conversion.Identity;

                        if (liftedFromConversion.Exists && liftedToConversion.Exists)
                        {
                            u.Add(UserDefinedConversionAnalysis.Lifted(op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo));
                        }
                    }
                }
            }
        }
        private static ConversionKind ClassifyNullLiteralConversion(BoundExpression source, TypeSymbol destination)
        {
            Debug.Assert((object)source != null);
            Debug.Assert((object)destination != null);

            if (!source.IsLiteralNull())
            {
                return ConversionKind.NoConversion;
            }

            // SPEC: An implicit conversion exists from the null literal to any nullable type. 
            if (destination.IsNullableType())
            {
                // The spec defines a "null literal conversion" specifically as a conversion from
                // null to nullable type.
                return ConversionKind.NullLiteral;
            }

            // SPEC: An implicit conversion exists from the null literal to any reference type. 
            // SPEC: An implicit conversion exists from the null literal to type parameter T, 
            // SPEC: provided T is known to be a reference type. [...] The conversion [is] classified 
            // SPEC: as implicit reference conversion. 

            if (destination.IsReferenceType)
            {
                return ConversionKind.ImplicitReference;
            }

            // SPEC: The set of implicit conversions is extended to include...
            // SPEC: ... from the null literal to any pointer type.

            if (destination is PointerTypeSymbol)
            {
                return ConversionKind.NullToPointer;
            }

            return ConversionKind.NoConversion;
        }
Exemplo n.º 27
0
        private static bool SatisfiesConstraintType(
            ConversionsBase conversions,
            TypeSymbol typeArgument,
            TypeSymbol constraintType,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            if (constraintType.IsErrorType())
            {
                return false;
            }

            // Spec 4.4.4 describes the valid conversions from
            // type argument A to constraint type C:

            // "An identity conversion (6.1.1).
            // An implicit reference conversion (6.1.6). ..."
            if (conversions.HasIdentityOrImplicitReferenceConversion(typeArgument, constraintType, ref useSiteDiagnostics))
            {
                return true;
            }

            // "... A boxing conversion (6.1.7), provided that type A is a non-nullable value type. ..."
            // NOTE: we extend this to allow, for example, a conversion from Nullable<T> to object.
            if (typeArgument.IsValueType &&
                conversions.HasBoxingConversion(typeArgument.IsNullableType() ? ((NamedTypeSymbol)typeArgument).ConstructedFrom : typeArgument, constraintType, ref useSiteDiagnostics))
            {
                return true;
            }

            if (typeArgument.TypeKind == TypeKind.TypeParameter)
            {
                var typeParameter = (TypeParameterSymbol)typeArgument;

                // "... An implicit reference, boxing, or type parameter conversion
                // from type parameter A to C."
                if (conversions.HasImplicitTypeParameterConversion(typeParameter, constraintType, ref useSiteDiagnostics))
                {
                    return true;
                }

                // TypeBind::SatisfiesBound allows cases where one of the
                // type parameter constraints satisfies the constraint.
                foreach (var typeArgumentConstraint in typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
                {
                    if (SatisfiesConstraintType(conversions, typeArgumentConstraint, constraintType, ref useSiteDiagnostics))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
Exemplo n.º 28
0
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
            TypeSymbol operandType,
            TypeSymbol patternType,
            bool patternTypeWasInSource,
            bool isVar,
            DiagnosticBag diagnostics)
        {
            Debug.Assert((object)operandType != null);
            Debug.Assert((object)patternType != null);

            if (operandType.IsErrorType() || patternType.IsErrorType())
            {
                return(false);
            }
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return(true);
            }
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return(true);
            }
            else if (!isVar)
            {
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return(true);
                }

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                var matchPossible = ExpressionOfTypeMatchesPatternType(Conversions, operandType, patternType, ref useSiteDiagnostics, out Conversion conversion, operandConstantValue: null, operandCouldBeNull: true);
                diagnostics.Add(typeSyntax, useSiteDiagnostics);
                if (matchPossible != false)
                {
                    if (!conversion.Exists && (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter()))
                    {
                        // permit pattern-matching when one of the types is an open type in C# 7.1.
                        LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion();
                        if (requiredVersion > Compilation.LanguageVersion)
                        {
                            Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax,
                                  operandType, patternType,
                                  Compilation.LanguageVersion.ToDisplayString(),
                                  new CSharpRequiredLanguageVersion(requiredVersion));
                            return(true);
                        }
                    }
                }
                else
                {
                    Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
                    return(true);
                }
            }

            return(false);
        }
Exemplo n.º 29
0
        private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement)
        {
            BoundExpression result;
            // If we have a built-in increment or decrement then things get a bit trickier. Suppose for example we have
            // a user-defined conversion from X to short and from short to X, but no user-defined increment operator on
            // X.  The increment portion of "++x" is then: (X)(short)((int)(short)x + 1). That is, first x must be
            // converted to short via an implicit user- defined conversion, then to int via an implicit numeric
            // conversion, then the addition is performed in integers. The resulting integer is converted back to short,
            // and then the short is converted to X.

            // This is the input and output type of the unary increment operator we're going to call.
            // That is, "short" in the example above.
            TypeSymbol unaryOperandType = GetUnaryOperatorType(node);

            // This is the kind of binary operator that we're going to realize the unary operator
            // as. That is, "int + int --> int" in the example above.
            BinaryOperatorKind binaryOperatorKind = GetCorrespondingBinaryOperator(node);

            binaryOperatorKind |= IsIncrement(node) ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction;

            // The "1" in the example above.
            ConstantValue constantOne = GetConstantOneForBinOp(binaryOperatorKind);

            Debug.Assert(constantOne != null);
            Debug.Assert(constantOne.SpecialType != SpecialType.None);
            Debug.Assert(binaryOperatorKind.OperandTypes() != 0);

            // The input/output type of the binary operand. "int" in the example.
            TypeSymbol binaryOperandType = _compilation.GetSpecialType(constantOne.SpecialType);

            // 1
            BoundExpression boundOne = MakeLiteral(
                syntax: node.Syntax,
                constantValue: constantOne,
                type: binaryOperandType);

            if (binaryOperatorKind.IsLifted())
            {
                binaryOperandType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(binaryOperandType);
                MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor);
                boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, boundOne);
            }

            // Now we construct the other operand to the binary addition. We start with just plain "x".
            BoundExpression binaryOperand = rewrittenValueToIncrement;

            bool @checked = node.OperatorKind.IsChecked();

            // If we need to make a conversion from the original operand type to the operand type of the
            // underlying increment operation, do it now.
            if (!node.OperandConversion.IsIdentity)
            {
                // (short)x
                binaryOperand = MakeConversionNode(
                    syntax: node.Syntax,
                    rewrittenOperand: binaryOperand,
                    conversion: node.OperandConversion,
                    rewrittenType: unaryOperandType,
                    @checked: @checked);
            }

            // Early-out for pointer increment - we don't need to convert the operands to a common type.
            if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.Pointer)
            {
                Debug.Assert(binaryOperatorKind.OperandTypes() == BinaryOperatorKind.PointerAndInt);
                Debug.Assert(binaryOperand.Type.IsPointerType());
                Debug.Assert(boundOne.Type.SpecialType == SpecialType.System_Int32);
                return(MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperand.Type, method: null));
            }

            // If we need to make a conversion from the unary operator type to the binary operator type,
            // do it now.

            // (int)(short)x
            binaryOperand = MakeConversionNode(binaryOperand, binaryOperandType, @checked);

            // Perform the addition.

            // (int)(short)x + 1
            BoundExpression binOp;

            if (unaryOperandType.SpecialType == SpecialType.System_Decimal)
            {
                binOp = MakeDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand);
            }
            else if (unaryOperandType.IsNullableType() && unaryOperandType.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal)
            {
                binOp = MakeLiftedDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand);
            }
            else
            {
                binOp = MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperandType, method: null);
            }

            // Generate the conversion back to the type of the unary operator.

            // (short)((int)(short)x + 1)
            result = MakeConversionNode(binOp, unaryOperandType, @checked);
            return(result);
        }
Exemplo n.º 30
0
        private bool LowerBoundNullableInference(TypeSymbol source, TypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert((object)source != null);
            Debug.Assert((object)target != null);

            if (!source.IsNullableType() || !target.IsNullableType())
            {
                return false;
            }

            LowerBoundInference(source.GetNullableUnderlyingType(), target.GetNullableUnderlyingType(), ref useSiteDiagnostics);
            return true;
        }
Exemplo n.º 31
0
        // in simple cases could be left unlowered.
        // IL gen can generate more compact code for unlowered conditional accesses
        // by utilizing stack dup/pop instructions
        public override BoundNode VisitConditionalAccess(BoundConditionalAccess node)
        {
            BoundExpression loweredReceiver = (BoundExpression)this.Visit(node.Receiver);

            var receiverType = loweredReceiver.Type;

            //TODO: if AccessExpression does not contain awaits, the node could be left unlowered (saves a temp),
            //      but there seem to be no way of knowing that without walking AccessExpression.
            var needToLower = receiverType.IsNullableType() || this.inExpressionLambda || this.factory.CurrentMethod.IsAsync;

            var         previousConditionalAccesTarget = currentConditionalAccessTarget;
            LocalSymbol temp = null;

            if (needToLower)
            {
                if (NeedsTemp(loweredReceiver, localsMayBeAssigned: false))
                {
                    temp = factory.SynthesizedLocal(receiverType);
                    currentConditionalAccessTarget = factory.Local(temp);
                    loweredReceiver = factory.AssignmentExpression(factory.Local(temp), loweredReceiver);
                }
                else
                {
                    currentConditionalAccessTarget = loweredReceiver;
                }
            }
            else
            {
                currentConditionalAccessTarget = null;
            }

            BoundExpression loweredAccessExpression = (BoundExpression)this.Visit(node.AccessExpression);

            currentConditionalAccessTarget = previousConditionalAccesTarget;

            TypeSymbol type = this.VisitType(node.Type);

            TypeSymbol nodeType             = node.Type;
            TypeSymbol accessExpressionType = loweredAccessExpression.Type;

            if (accessExpressionType != nodeType)
            {
                Debug.Assert(nodeType.IsNullableType() && accessExpressionType == nodeType.GetNullableUnderlyingType());
                loweredAccessExpression = factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression);
            }

            BoundExpression result;

            if (!needToLower)
            {
                Debug.Assert(receiverType.IsReferenceType);
                result = node.Update(loweredReceiver, loweredAccessExpression, type);
            }
            else
            {
                var condition = receiverType.IsReferenceType ?
                                factory.ObjectNotEqual(loweredReceiver, factory.Null(receiverType)) :
                                MakeOptimizedHasValue(loweredReceiver.Syntax, loweredReceiver);

                var consequence = loweredAccessExpression;
                var alternative = factory.Default(nodeType);

                result = RewriteConditionalOperator(node.Syntax,
                                                    condition,
                                                    consequence,
                                                    alternative,
                                                    null,
                                                    alternative.Type);

                if (temp != null)
                {
                    result = factory.Sequence(temp, result);
                }
            }

            return(result);
        }
Exemplo n.º 32
0
        /// <summary>
        /// Check that the pattern type is valid for the operand. Return true if an error was reported.
        /// </summary>
        private bool CheckValidPatternType(
            CSharpSyntaxNode typeSyntax,
            BoundExpression operand,
            TypeSymbol operandType,
            TypeSymbol patternType,
            bool patternTypeWasInSource,
            bool isVar,
            DiagnosticBag diagnostics)
        {
            Debug.Assert((object)operandType != null);
            Debug.Assert((object)patternType != null);

            // Because we do not support recursive patterns, we always have an operand
            Debug.Assert((object)operand != null);
            // Once we support recursive patterns that will be relaxed.

            if (operandType.IsErrorType() || patternType.IsErrorType())
            {
                return(false);
            }
            else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource)
            {
                // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type.
                Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType());
                return(true);
            }
            else if (patternType.IsStatic)
            {
                Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType);
                return(true);
            }
            else if (!isVar)
            {
                if (patternType.IsDynamic())
                {
                    Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
                    return(true);
                }

                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                Conversion conversion =
                    operand != null
                    ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast : true)
                    : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true);

                diagnostics.Add(typeSyntax, useSiteDiagnostics);
                switch (conversion.Kind)
                {
                case ConversionKind.ExplicitDynamic:
                case ConversionKind.ImplicitDynamic:
                // Since the input was `dynamic`, which is equivalent to `object`, there must also
                // exist some unboxing, identity, or reference conversion as well, making the conversion legal.
                case ConversionKind.Boxing:
                case ConversionKind.ExplicitNullable:
                case ConversionKind.ExplicitReference:
                case ConversionKind.Identity:
                case ConversionKind.ImplicitReference:
                case ConversionKind.Unboxing:
                case ConversionKind.ImplicitNullable:
                    // these are the conversions allowed by a pattern match
                    break;

                case ConversionKind.DefaultOrNullLiteral:
                    throw ExceptionUtilities.UnexpectedValue(conversion.Kind);

                //case ConversionKind.ExplicitNumeric:  // we do not perform numeric conversions of the operand
                //case ConversionKind.ImplicitConstant:
                //case ConversionKind.ImplicitNumeric:
                default:
                    Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType);
                    return(true);
                }
            }

            return(false);
        }
Exemplo n.º 33
0
 public static TypeSymbol StrippedType(this TypeSymbol type)
 {
     return(type.IsNullableType() ? type.GetNullableUnderlyingType() : type);
 }
        public static string GetErrorReportingName(TypeSymbol type, RefKind refKind = RefKind.None)
        {
            string prefix = "";
            switch (refKind)
            {
                case RefKind.Ref: prefix = "ref "; break;
                case RefKind.Out: prefix = "out "; break;
            }

            switch (type.GetSpecialTypeSafe())
            {
                case SpecialType.System_Void: 
                case SpecialType.System_SByte:
                case SpecialType.System_Int16:
                case SpecialType.System_Int32:
                case SpecialType.System_Int64:
                case SpecialType.System_Byte: 
                case SpecialType.System_UInt16:
                case SpecialType.System_UInt32:
                case SpecialType.System_UInt64:
                case SpecialType.System_Single:
                case SpecialType.System_Double:
                case SpecialType.System_Decimal:
                case SpecialType.System_Char:
                case SpecialType.System_Boolean:
                case SpecialType.System_String:
                case SpecialType.System_Object:
                    return prefix + SemanticFacts.GetLanguageName(type.SpecialType);

                case SpecialType.None:
                    if (type != null && type.IsNullableType() && !ReferenceEquals(type, type.OriginalDefinition))
                    {
                        TypeSymbol underlyingType = type.GetNullableUnderlyingType();

                        switch (underlyingType.GetSpecialTypeSafe())
                        {
                            case SpecialType.System_Boolean:
                            case SpecialType.System_SByte:
                            case SpecialType.System_Int16:
                            case SpecialType.System_Int32:
                            case SpecialType.System_Int64:
                            case SpecialType.System_Byte:
                            case SpecialType.System_UInt16:
                            case SpecialType.System_UInt32:
                            case SpecialType.System_UInt64:
                            case SpecialType.System_Single:
                            case SpecialType.System_Double:
                            case SpecialType.System_Decimal:
                            case SpecialType.System_Char:
                                return prefix + SemanticFacts.GetLanguageName(underlyingType.SpecialType) + "?";
                        }

                        return prefix + GetErrorReportingName(underlyingType) + "?";
                    }

                    break;
            }

            var dynamicType = type as DynamicTypeSymbol;
            if (dynamicType != null)
            {
                return prefix + "dynamic";
            }

            var arrayType = type as ArrayTypeSymbol;
            if (arrayType != null)
            {
                string suffix = "";
                while (true)
                {
                    var elementType = arrayType.ElementType;
                    suffix += GetSuffix(arrayType.Rank);
                    arrayType = elementType as ArrayTypeSymbol;
                    if (arrayType == null)
                    {
                        return prefix + GetErrorReportingName(elementType) + suffix;
                    }
                }
            }

            var pointerType = type as PointerTypeSymbol;
            if (pointerType != null)
            {
                return prefix + GetErrorReportingName(pointerType.BaseType) + "*";
            }

            var namedType = type as NamedTypeSymbol;
            if (namedType != null)
            {
                string result = "";
                if (namedType.ContainingType != null)
                {
                    result = GetErrorReportingName(namedType.ContainingType) + ".";
                }
                else if (namedType.ContainingNamespace != null && !namedType.ContainingNamespace.IsGlobalNamespace)
                {
                    result = namedType.ContainingNamespace.GetFullName() + ".";
                }
                result += type.Name;
                if (namedType.TypeArguments.Count != 0)
                {
                    result += "<";
                    result += namedType.TypeArguments.Select(a => GetErrorReportingName(a)).Comma(",");
                    result += ">";
                }
                return prefix + result;
            }

            var typeParameter = type as TypeParameterSymbol;
            if (typeParameter != null)
            {
                return prefix + type.Name;
            }

            Debug.Fail("What case did we miss in type name error reporter?");
            return prefix + type.GetFullName();
        }
Exemplo n.º 35
0
        // See TypeBind::CheckSingleConstraint.
        private static bool CheckConstraints(
            Symbol containingSymbol,
            ConversionsBase conversions,
            TypeMap substitution,
            TypeParameterSymbol typeParameter,
            TypeSymbol typeArgument,
            Compilation currentCompilation,
            ArrayBuilder<TypeParameterDiagnosticInfo> diagnosticsBuilder,
            ref ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder,
            HashSet<TypeParameterSymbol> ignoreTypeConstraintsDependentOnTypeParametersOpt)
        {
            Debug.Assert(substitution != null);
            // The type parameters must be original definitions of type parameters from the containing symbol.
            Debug.Assert(ReferenceEquals(typeParameter.ContainingSymbol, containingSymbol.OriginalDefinition));

            if (typeArgument.IsErrorType())
            {
                return true;
            }

            if (typeArgument.IsPointerType() || typeArgument.IsRestrictedType() || typeArgument.SpecialType == SpecialType.System_Void)
            {
                // "The type '{0}' may not be used as a type argument"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_BadTypeArgument, typeArgument)));
                return false;
            }

            if (typeArgument.IsStatic)
            {
                // "'{0}': static types cannot be used as type arguments"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_GenericArgIsStaticClass, typeArgument)));
                return false;
            }

            if (typeParameter.HasReferenceTypeConstraint && !typeArgument.IsReferenceType)
            {
                // "The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}'"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_RefConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument)));
                return false;
            }

            if (typeParameter.HasValueTypeConstraint && !typeArgument.IsNonNullableValueType())
            {
                // "The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}'"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_ValConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument)));
                return false;
            }

            // The type parameters for a constructed type/method are the type parameters of
            // the ConstructedFrom type/method, so the constraint types are not substituted.
            // For instance with "class C<T, U> where T : U", the type parameter for T in "C<object, int>"
            // has constraint "U", not "int". We need to substitute the constraints from the
            // original definition of the type parameters using the map from the constructed symbol.
            var constraintTypes = ArrayBuilder<TypeSymbol>.GetInstance();
            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
            substitution.SubstituteTypesDistinctWithoutModifiers(typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics), constraintTypes, 
                                                                 ignoreTypeConstraintsDependentOnTypeParametersOpt);

            bool hasError = false;

            foreach (var constraintType in constraintTypes)
            {
                if (SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics))
                {
                    continue;
                }

                ErrorCode errorCode;
                if (typeArgument.IsReferenceType)
                {
                    errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType;
                }
                else if (typeArgument.IsNullableType())
                {
                    errorCode = constraintType.IsInterfaceType() ? ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface : ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum;
                }
                else if (typeArgument.TypeKind == TypeKind.TypeParameter)
                {
                    errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar;
                }
                else
                {
                    errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedValType;
                }

                SymbolDistinguisher distinguisher = new SymbolDistinguisher(currentCompilation, constraintType, typeArgument);
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(errorCode, containingSymbol.ConstructedFrom(), distinguisher.First, typeParameter, distinguisher.Second)));
                hasError = true;
            }

            if (AppendUseSiteDiagnostics(useSiteDiagnostics, typeParameter, ref useSiteDiagnosticsBuilder))
            {
                hasError = true;
            }

            constraintTypes.Free();

            // Check the constructor constraint.
            if (typeParameter.HasConstructorConstraint && !SatisfiesConstructorConstraint(typeArgument))
            {
                // "'{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}'"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_NewConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument)));
                return false;
            }

            return !hasError;
        }
        /// <summary>
        /// Helper method to generate a lowered conversion.
        /// </summary>
        private BoundExpression MakeConversion(
            BoundConversion oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            MethodSymbol symbolOpt,
            bool @checked,
            bool explicitCastInCode,
            bool isExtensionMethod,
            bool isArrayIndex,
            ConstantValue constantValueOpt,
            TypeSymbol rewrittenType)
        {
            Debug.Assert(oldNode == null || oldNode.Syntax == syntax);
            Debug.Assert((object)rewrittenType != null);
            @checked = @checked &&
                (inExpressionLambda && (explicitCastInCode || DistinctSpecialTypes(rewrittenOperand.Type, rewrittenType)) ||
                NeedsChecked(rewrittenOperand.Type, rewrittenType));

            switch (conversionKind)
            {
                case ConversionKind.Identity:

                    // Spec 6.1.1:
                    //   An identity conversion converts from any type to the same type. 
                    //   This conversion exists such that an entity that already has a required type can be said to be convertible to that type.
                    //   Because object and dynamic are considered equivalent there is an identity conversion between object and dynamic, 
                    //   and between constructed types that are the same when replacing all occurrences of dynamic with object.

                    // Why ignoreDynamic: false?
                    // Lowering phase treats object and dynamic as equivalent types. So we don't need to produce any conversion here,
                    // but we need to change the Type property on the resulting BoundExpression to match the rewrittenType.
                    // This is necessary so that subsequent lowering transformations see that the expression is dynamic.

                    if (inExpressionLambda || !rewrittenOperand.Type.Equals(rewrittenType, ignoreCustomModifiers: false, ignoreDynamic: false))
                    {
                        break;
                    }

                    // 4.1.6 C# spec: To force a value of a floating point type to the exact precision of its type, an explicit cast can be used.
                    // If this is not an identity conversion of a float with unknown precision, strip away the identity conversion.
                    if (!(explicitCastInCode && IsFloatPointExpressionOfUnknownPrecision(rewrittenOperand)))
                    {
                        return rewrittenOperand;
                    }

                    break;

                case ConversionKind.ExplicitUserDefined:
                case ConversionKind.ImplicitUserDefined:
                    return RewriteUserDefinedConversion(
                        syntax: syntax,
                        rewrittenOperand: rewrittenOperand,
                        method: symbolOpt,
                        rewrittenType: rewrittenType,
                        conversionKind: conversionKind);

                case ConversionKind.IntPtr:
                    return RewriteIntPtrConversion(oldNode, syntax, rewrittenOperand, conversionKind, symbolOpt, @checked,
                        explicitCastInCode, isExtensionMethod, isArrayIndex, constantValueOpt, rewrittenType);

                case ConversionKind.ImplicitNullable:
                case ConversionKind.ExplicitNullable:
                    return RewriteNullableConversion(
                        syntax: syntax,
                        rewrittenOperand: rewrittenOperand,
                        conversionKind: conversionKind,
                        @checked: @checked,
                        explicitCastInCode: explicitCastInCode,
                        rewrittenType: rewrittenType);

                case ConversionKind.Boxing:

                    if (!inExpressionLambda)
                    {
                        // We can perform some optimizations if we have a nullable value type
                        // as the operand and we know its nullability:

                        // * (object)new int?() is the same as (object)null
                        // * (object)new int?(123) is the same as (object)123

                        if (NullableNeverHasValue(rewrittenOperand))
                        {
                            return new BoundDefaultOperator(syntax, rewrittenType);
                        }

                        BoundExpression nullableValue = NullableAlwaysHasValue(rewrittenOperand);
                        if (nullableValue != null)
                        {
                            // Recurse, eliminating the unnecessary ctor.
                            return MakeConversion(oldNode, syntax, nullableValue, conversionKind,
                                symbolOpt, @checked, explicitCastInCode, isExtensionMethod, isArrayIndex,
                                constantValueOpt, rewrittenType);
                        }
                    }
                    break;

                case ConversionKind.NullLiteral:
                    if (!inExpressionLambda || !explicitCastInCode)
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    break;

                case ConversionKind.ImplicitReference:
                case ConversionKind.ExplicitReference:
                    if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode))
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    break;

                case ConversionKind.ImplicitNumeric:
                case ConversionKind.ExplicitNumeric:
                    if (rewrittenOperand.IsDefaultValue() && (!inExpressionLambda || !explicitCastInCode))
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    if (rewrittenType.SpecialType == SpecialType.System_Decimal || rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal)
                    {
                        return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, rewrittenType);
                    }
                    break;

                case ConversionKind.ImplicitEnumeration:
                    // A conversion from constant zero to nullable is actually classified as an 
                    // implicit enumeration conversion, not an implicit nullable conversion. 
                    // Lower it to (E?)(E)0.
                    if (rewrittenType.IsNullableType())
                    {
                        var operand = MakeConversion(
                            oldNode,
                            syntax,
                            rewrittenOperand,
                            ConversionKind.ImplicitEnumeration,
                            symbolOpt,
                            @checked,
                            explicitCastInCode,
                            isExtensionMethod,
                            false,
                            constantValueOpt,
                            rewrittenType.GetNullableUnderlyingType());

                        return MakeConversion(
                            oldNode,
                            syntax,
                            operand,
                            ConversionKind.ImplicitNullable,
                            symbolOpt,
                            @checked,
                            explicitCastInCode,
                            isExtensionMethod,
                            isArrayIndex,
                            constantValueOpt,
                            rewrittenType);
                    }

                    goto case ConversionKind.ExplicitEnumeration;

                case ConversionKind.ExplicitEnumeration:
                    if (!rewrittenType.IsNullableType() &&
                        rewrittenOperand.IsDefaultValue() &&
                        (!inExpressionLambda || !explicitCastInCode))
                    {
                        return new BoundDefaultOperator(syntax, rewrittenType);
                    }

                    if (rewrittenType.SpecialType == SpecialType.System_Decimal)
                    {
                        Debug.Assert(rewrittenOperand.Type.IsEnumType());
                        var underlyingTypeFrom = rewrittenOperand.Type.GetEnumUnderlyingType();
                        rewrittenOperand = MakeConversion(rewrittenOperand, underlyingTypeFrom, false);
                        return RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, underlyingTypeFrom, rewrittenType);
                    }
                    else if (rewrittenOperand.Type.SpecialType == SpecialType.System_Decimal)
                    {
                        // This is where we handle conversion from Decimal to Enum: e.g., E e = (E) d;
                        // where 'e' is of type Enum E and 'd' is of type Decimal.
                        // Conversion can be simply done by applying its underlying numeric type to RewriteDecimalConversion(). 

                        Debug.Assert(rewrittenType.IsEnumType());
                        var underlyingTypeTo = rewrittenType.GetEnumUnderlyingType();
                        var rewrittenNode = RewriteDecimalConversion(oldNode, syntax, rewrittenOperand, rewrittenOperand.Type, underlyingTypeTo);

                        // However, the type of the rewritten node becomes underlying numeric type, not Enum type,
                        // which violates the overall constraint saying the type cannot be changed during rewriting (see LocalRewriter.cs).

                        // Instead of loosening this constraint, we return BoundConversion from underlying numeric type to Enum type,
                        // which will be eliminated during emitting (see EmitEnumConversion): e.g., E e = (E)(int) d;

                        return new BoundConversion(
                            syntax,
                            rewrittenNode,
                            conversionKind,
                            LookupResultKind.Viable,
                            isBaseConversion: false,
                            symbolOpt: symbolOpt,
                            @checked: false,
                            explicitCastInCode: explicitCastInCode,
                            isExtensionMethod: isExtensionMethod,
                            isArrayIndex: false,
                            constantValueOpt: constantValueOpt,
                            type: rewrittenType);
                    }

                    break;

                case ConversionKind.ImplicitDynamic:
                case ConversionKind.ExplicitDynamic:
                    Debug.Assert((object)symbolOpt == null);
                    Debug.Assert(!isExtensionMethod);
                    Debug.Assert(constantValueOpt == null);
                    return dynamicFactory.MakeDynamicConversion(rewrittenOperand, explicitCastInCode || conversionKind == ConversionKind.ExplicitDynamic, isArrayIndex, @checked, rewrittenType).ToExpression();

                default:
                    break;
            }

            return oldNode != null ?
                oldNode.Update(
                    rewrittenOperand,
                    conversionKind,
                    oldNode.ResultKind,
                    isBaseConversion: oldNode.IsBaseConversion,
                    symbolOpt: symbolOpt,
                    @checked: @checked,
                    explicitCastInCode: explicitCastInCode,
                    isExtensionMethod: isExtensionMethod,
                    isArrayIndex: isArrayIndex,
                    constantValueOpt: constantValueOpt,
                    type: rewrittenType) :
                new BoundConversion(
                    syntax,
                    rewrittenOperand,
                    conversionKind,
                    LookupResultKind.Viable,
                    isBaseConversion: false,
                    symbolOpt: symbolOpt,
                    @checked: @checked,
                    explicitCastInCode: explicitCastInCode,
                    isExtensionMethod: isExtensionMethod,
                    isArrayIndex: isArrayIndex,
                    constantValueOpt: constantValueOpt,
                    type: rewrittenType);
        }
Exemplo n.º 37
0
        internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSyntaxNode node, BoundExpression expression, ref ConstantValue constantValue, DiagnosticBag diagnostics)
        {
            // NOTE: This will allow user-defined conversions, even though they're not allowed here.  This is acceptable
            // because the result of a user-defined conversion does not have a ConstantValue and we'll report a diagnostic
            // to that effect later.
            BoundExpression convertedExpression = GenerateConversionForAssignment(inputType, expression, diagnostics);

            if (convertedExpression.Kind == BoundKind.Conversion)
            {
                var conversion = (BoundConversion)convertedExpression;
                var operand = conversion.Operand;
                if (inputType.IsNullableType() && (convertedExpression.ConstantValue == null || !convertedExpression.ConstantValue.IsNull))
                {
                    // Null is a special case here because we want to compare null to the Nullable<T> itself, not to the underlying type.
                    var discardedDiagnostics = DiagnosticBag.GetInstance(); // We are not intested in the diagnostic that get created here
                    convertedExpression = CreateConversion(operand, inputType.GetNullableUnderlyingType(), discardedDiagnostics);
                    discardedDiagnostics.Free();
                }
                else if ((conversion.ConversionKind == ConversionKind.Boxing || conversion.ConversionKind == ConversionKind.ImplicitReference)
                    && operand.ConstantValue != null && convertedExpression.ConstantValue == null)
                {
                    // A boxed constant (or string converted to object) is a special case because we prefer
                    // to compare to the pre-converted value by casting the input value to the type of the constant
                    // (that is, unboxing or downcasting it) and then testing the resulting value using primitives.
                    // That is much more efficient than calling object.Equals(x, y), and we can share the downcasted
                    // input value among many constant tests.
                    convertedExpression = operand;
                }
            }

            constantValue = convertedExpression.ConstantValue;
            return convertedExpression;
        }
            private static int? TypeToIndex(TypeSymbol type)
            {
                switch (type.GetSpecialTypeSafe())
                {
                    case SpecialType.System_Object: return 0;
                    case SpecialType.System_String: return 1;
                    case SpecialType.System_Boolean: return 2;
                    case SpecialType.System_Char: return 3;
                    case SpecialType.System_SByte: return 4;
                    case SpecialType.System_Int16: return 5;
                    case SpecialType.System_Int32: return 6;
                    case SpecialType.System_Int64: return 7;
                    case SpecialType.System_Byte: return 8;
                    case SpecialType.System_UInt16: return 9;
                    case SpecialType.System_UInt32: return 10;
                    case SpecialType.System_UInt64: return 11;
                    case SpecialType.System_Single: return 12;
                    case SpecialType.System_Double: return 13;
                    case SpecialType.System_Decimal: return 14;

                    case SpecialType.None:
                        if (type != null && type.IsNullableType())
                        {
                            TypeSymbol underlyingType = type.GetNullableUnderlyingType();

                            switch (underlyingType.GetSpecialTypeSafe())
                            {
                                case SpecialType.System_Boolean: return 15;
                                case SpecialType.System_Char: return 16;
                                case SpecialType.System_SByte: return 17;
                                case SpecialType.System_Int16: return 18;
                                case SpecialType.System_Int32: return 19;
                                case SpecialType.System_Int64: return 20;
                                case SpecialType.System_Byte: return 21;
                                case SpecialType.System_UInt16: return 22;
                                case SpecialType.System_UInt32: return 23;
                                case SpecialType.System_UInt64: return 24;
                                case SpecialType.System_Single: return 25;
                                case SpecialType.System_Double: return 26;
                                case SpecialType.System_Decimal: return 27;
                            }
                        }

                        // fall through
                        goto default;

                    default: return null;
                }
            }
        public ConstantValue FoldConstantConversion(
            CSharpSyntaxNode syntax,
            BoundExpression source,
            Conversion conversion,
            TypeSymbol destination,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(source != null);
            Debug.Assert((object)destination != null);

            // The diagnostics bag can be null in cases where we know ahead of time that the
            // conversion will succeed without error or warning. (For example, if we have a valid
            // implicit numeric conversion on a constant of numeric type.)

            // SPEC: A constant expression must be the null literal or a value with one of 
            // SPEC: the following types: sbyte, byte, short, ushort, int, uint, long, 
            // SPEC: ulong, char, float, double, decimal, bool, string, or any enumeration type.

            // SPEC: The following conversions are permitted in constant expressions:
            // SPEC: Identity conversions
            // SPEC: Numeric conversions
            // SPEC: Enumeration conversions
            // SPEC: Constant expression conversions
            // SPEC: Implicit and explicit reference conversions, provided that the source of the conversions 
            // SPEC: is a constant expression that evaluates to the null value.

            // SPEC VIOLATION: C# has always allowed the following, even though this does violate the rule that
            // SPEC VIOLATION: a constant expression must be either the null literal, or an expression of one 
            // SPEC VIOLATION: of the given types. 

            // SPEC VIOLATION: const C c = (C)null;

            // TODO: Some conversions can produce errors or warnings depending on checked/unchecked.
            // TODO: Fold conversions on enums and strings too.

            if (source.HasAnyErrors)
            {
                return null;
            }

            var sourceConstantValue = source.ConstantValue;
            if (sourceConstantValue == null || sourceConstantValue.IsBad)
            {
                return sourceConstantValue;
            }

            switch (conversion.Kind)
            {
                case ConversionKind.Identity:
                case ConversionKind.NullLiteral:
                    return sourceConstantValue;

                case ConversionKind.ImplicitConstant:
                    return FoldConstantNumericConversion(syntax, sourceConstantValue, destination, diagnostics);

                case ConversionKind.ExplicitNumeric:
                case ConversionKind.ImplicitNumeric:
                case ConversionKind.ExplicitEnumeration:
                case ConversionKind.ImplicitEnumeration:
                    // The C# specification categorizes conversion from literal zero to nullable enum as 
                    // an Implicit Enumeration Conversion. Such a thing should not be constant folded
                    // because nullable enums are never constants.

                    if (destination.IsNullableType())
                    {
                        return null;
                    }

                    return FoldConstantNumericConversion(syntax, sourceConstantValue, destination, diagnostics);

                case ConversionKind.ExplicitReference:
                case ConversionKind.ImplicitReference:
                    return sourceConstantValue.IsNull ? sourceConstantValue : null;
            }

            return null;
        }
        private TypeSymbol TypeOrUnderlyingType(TypeSymbol type)
        {
            if (type.IsNullableType())
            {
                return type.GetNullableUnderlyingType();
            }

            return type;
        }
        private static LiftingResult UserDefinedBinaryOperatorCanBeLifted(TypeSymbol left, TypeSymbol right, TypeSymbol result, BinaryOperatorKind kind)
        {
            // SPEC: For the binary operators + - * / % & | ^ << >> a lifted form of the
            // SPEC: operator exists if the operand and result types are all non-nullable
            // SPEC: value types. The lifted form is constructed by adding a single ?
            // SPEC: modifier to each operand and result type. 
            //
            // SPEC: For the equality operators == != a lifted form of the operator exists
            // SPEC: if the operand types are both non-nullable value types and if the 
            // SPEC: result type is bool. The lifted form is constructed by adding
            // SPEC: a single ? modifier to each operand type.
            //
            // SPEC: For the relational operators > < >= <= a lifted form of the 
            // SPEC: operator exists if the operand types are both non-nullable value
            // SPEC: types and if the result type is bool. The lifted form is 
            // SPEC: constructed by adding a single ? modifier to each operand type.

            if (!left.IsValueType ||
                left.IsNullableType() ||
                !right.IsValueType ||
                right.IsNullableType())
            {
                return LiftingResult.NotLifted;
            }

            switch (kind)
            {
                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                    // Spec violation: can't lift unless the types match.
                    // The spec doesn't require this, but dev11 does and it reduces ambiguity in some cases.
                    if (left != right) return LiftingResult.NotLifted;
                    goto case BinaryOperatorKind.GreaterThan;
                case BinaryOperatorKind.GreaterThan:
                case BinaryOperatorKind.GreaterThanOrEqual:
                case BinaryOperatorKind.LessThan:
                case BinaryOperatorKind.LessThanOrEqual:
                    return result.SpecialType == SpecialType.System_Boolean ?
                        LiftingResult.LiftOperandsButNotResult :
                        LiftingResult.NotLifted;
                default:
                    return result.IsValueType && !result.IsNullableType() ?
                        LiftingResult.LiftOperandsAndResult :
                        LiftingResult.NotLifted;
            }
        }
Exemplo n.º 42
0
        // See TypeBind::CheckSingleConstraint.
        private static bool CheckConstraints(
            Symbol containingSymbol,
            ConversionsBase conversions,
            TypeMap substitution,
            TypeParameterSymbol typeParameter,
            TypeSymbol typeArgument,
            Compilation currentCompilation,
            ArrayBuilder <TypeParameterDiagnosticInfo> diagnosticsBuilder,
            ref ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder)
        {
            Debug.Assert(substitution != null);
            // The type parameters must be original definitions of type parameters from the containing symbol.
            Debug.Assert(ReferenceEquals(typeParameter.ContainingSymbol, containingSymbol.OriginalDefinition));

            if (typeArgument.IsErrorType())
            {
                return(true);
            }

            if (typeArgument.IsPointerType() || typeArgument.IsRestrictedType() || typeArgument.SpecialType == SpecialType.System_Void)
            {
                // "The type '{0}' may not be used as a type argument"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_BadTypeArgument, typeArgument)));
                return(false);
            }

            if (typeArgument.IsStatic)
            {
                // "'{0}': static types cannot be used as type arguments"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_GenericArgIsStaticClass, typeArgument)));
                return(false);
            }

            if (typeParameter.HasReferenceTypeConstraint && !typeArgument.IsReferenceType)
            {
                // "The type '{2}' must be a reference type in order to use it as parameter '{1}' in the generic type or method '{0}'"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_RefConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument)));
                return(false);
            }

            if (typeParameter.HasValueTypeConstraint && !typeArgument.IsNonNullableValueType())
            {
                // "The type '{2}' must be a non-nullable value type in order to use it as parameter '{1}' in the generic type or method '{0}'"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_ValConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument)));
                return(false);
            }

            // The type parameters for a constructed type/method are the type parameters of
            // the ConstructedFrom type/method, so the constraint types are not substituted.
            // For instance with "class C<T, U> where T : U", the type parameter for T in "C<object, int>"
            // has constraint "U", not "int". We need to substitute the constraints from the
            // original definition of the type parameters using the map from the constructed symbol.
            var constraintTypes = ArrayBuilder <TypeSymbol> .GetInstance();

            HashSet <DiagnosticInfo> useSiteDiagnostics = null;

            substitution.SubstituteTypesDistinctWithoutModifiers(typeParameter.ConstraintTypesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics), constraintTypes);

            bool hasError = false;

            foreach (var constraintType in constraintTypes)
            {
                if (SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics))
                {
                    continue;
                }

                ErrorCode errorCode;
                if (typeArgument.IsReferenceType)
                {
                    errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedRefType;
                }
                else if (typeArgument.IsNullableType())
                {
                    errorCode = constraintType.IsInterfaceType() ? ErrorCode.ERR_GenericConstraintNotSatisfiedNullableInterface : ErrorCode.ERR_GenericConstraintNotSatisfiedNullableEnum;
                }
                else if (typeArgument.TypeKind == TypeKind.TypeParameter)
                {
                    errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedTyVar;
                }
                else
                {
                    errorCode = ErrorCode.ERR_GenericConstraintNotSatisfiedValType;
                }

                SymbolDistinguisher distinguisher = new SymbolDistinguisher(currentCompilation, constraintType, typeArgument);
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(errorCode, containingSymbol.ConstructedFrom(), distinguisher.First, typeParameter, distinguisher.Second)));
                hasError = true;
            }

            if (AppendUseSiteDiagnostics(useSiteDiagnostics, typeParameter, ref useSiteDiagnosticsBuilder))
            {
                hasError = true;
            }

            constraintTypes.Free();

            // Check the constructor constraint.
            if (typeParameter.HasConstructorConstraint && !SatisfiesConstructorConstraint(typeArgument))
            {
                // "'{2}' must be a non-abstract type with a public parameterless constructor in order to use it as parameter '{1}' in the generic type or method '{0}'"
                diagnosticsBuilder.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.ERR_NewConstraintNotSatisfied, containingSymbol.ConstructedFrom(), typeParameter, typeArgument)));
                return(false);
            }

            return(!hasError);
        }
        // IL gen can generate more compact code for certain conditional accesses
        // by utilizing stack dup/pop instructions
        internal BoundExpression RewriteConditionalAccess(BoundConditionalAccess node, bool used)
        {
            Debug.Assert(!_inExpressionLambda);

            var loweredReceiver = this.VisitExpression(node.Receiver);
            var receiverType    = loweredReceiver.Type;

            // Check trivial case
            if (loweredReceiver.IsDefaultValue() && receiverType.IsReferenceType)
            {
                return(_factory.Default(node.Type));
            }

            ConditionalAccessLoweringKind loweringKind;
            // dynamic receivers are not directly supported in codegen and need to be lowered to a ternary
            var lowerToTernary = node.AccessExpression.Type.IsDynamic();

            if (!lowerToTernary)
            {
                // trivial cases are directly supported in IL gen
                loweringKind = ConditionalAccessLoweringKind.LoweredConditionalAccess;
            }
            else if (CanChangeValueBetweenReads(loweredReceiver))
            {
                // NOTE: dynamic operations historically do not propagate mutations
                // to the receiver if that happens to be a value type
                // so we can capture receiver by value in dynamic case regardless of
                // the type of receiver
                // Nullable receivers are immutable so should be captured by value as well.
                loweringKind = ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal;
            }
            else
            {
                loweringKind = ConditionalAccessLoweringKind.Ternary;
            }


            var previousConditionalAccessTarget = _currentConditionalAccessTarget;
            var currentConditionalAccessID      = ++_currentConditionalAccessID;

            LocalSymbol temp = null;

            switch (loweringKind)
            {
            case ConditionalAccessLoweringKind.LoweredConditionalAccess:
                _currentConditionalAccessTarget = new BoundConditionalReceiver(
                    loweredReceiver.Syntax,
                    currentConditionalAccessID,
                    receiverType);

                break;

            case ConditionalAccessLoweringKind.Ternary:
                _currentConditionalAccessTarget = loweredReceiver;
                break;

            case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal:
                temp = _factory.SynthesizedLocal(receiverType);
                _currentConditionalAccessTarget = _factory.Local(temp);
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(loweringKind);
            }

            BoundExpression loweredAccessExpression;

            if (used)
            {
                loweredAccessExpression = this.VisitExpression(node.AccessExpression);
            }
            else
            {
                loweredAccessExpression = this.VisitUnusedExpression(node.AccessExpression);
                if (loweredAccessExpression == null)
                {
                    return(null);
                }
            }

            Debug.Assert(loweredAccessExpression != null);
            _currentConditionalAccessTarget = previousConditionalAccessTarget;

            TypeSymbol type = this.VisitType(node.Type);

            TypeSymbol nodeType             = node.Type;
            TypeSymbol accessExpressionType = loweredAccessExpression.Type;

            if (accessExpressionType.SpecialType == SpecialType.System_Void)
            {
                type = nodeType = accessExpressionType;
            }

            if (!TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) && nodeType.IsNullableType())
            {
                Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType.GetNullableUnderlyingType(), TypeCompareKind.ConsiderEverything2));
                loweredAccessExpression = _factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression);
            }
            else
            {
                Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) ||
                             (nodeType.SpecialType == SpecialType.System_Void && !used));
            }

            BoundExpression result;
            var             objectType = _compilation.GetSpecialType(SpecialType.System_Object);

            switch (loweringKind)
            {
            case ConditionalAccessLoweringKind.LoweredConditionalAccess:
                result = new BoundLoweredConditionalAccess(
                    node.Syntax,
                    loweredReceiver,
                    receiverType.IsNullableType() ?
                    UnsafeGetNullableMethod(node.Syntax, loweredReceiver.Type, SpecialMember.System_Nullable_T_get_HasValue) :
                    null,
                    loweredAccessExpression,
                    null,
                    currentConditionalAccessID,
                    type);

                break;

            case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal:
                // capture the receiver into a temp
                loweredReceiver = _factory.MakeSequence(
                    _factory.AssignmentExpression(_factory.Local(temp), loweredReceiver),
                    _factory.Local(temp));

                goto case ConditionalAccessLoweringKind.Ternary;

            case ConditionalAccessLoweringKind.Ternary:
            {
                // (object)r != null ? access : default(T)
                var condition = _factory.ObjectNotEqual(
                    _factory.Convert(objectType, loweredReceiver),
                    _factory.Null(objectType));

                var consequence = loweredAccessExpression;

                result = RewriteConditionalOperator(node.Syntax,
                                                    condition,
                                                    consequence,
                                                    _factory.Default(nodeType),
                                                    null,
                                                    nodeType,
                                                    isRef: false);

                if (temp != null)
                {
                    result = _factory.MakeSequence(temp, result);
                }
            }
            break;

            default:
                throw ExceptionUtilities.UnexpectedValue(loweringKind);
            }

            return(result);
        }
Exemplo n.º 44
0
        private bool LowerBoundNullableInference(TypeSymbol source, TypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert((object)source != null);
            Debug.Assert((object)target != null);

            // SPEC ISSUE: As noted above, the spec does not clearly call out how
            // SPEC ISSUE: to do type inference to a nullable target. I propose the
            // SPEC ISSUE: following:
            // SPEC ISSUE:
            // SPEC ISSUE: * Otherwise, if V is nullable type V1? and U is a 
            // SPEC ISSUE:   non-nullable struct type then an exact inference is made from U to V1.

            if (!target.IsNullableType() || !source.IsValueType || source.IsNullableType())
            {
                return false;
            }

            ExactInference(source, target.GetNullableUnderlyingType(), ref useSiteDiagnostics);
            return true;
        }
Exemplo n.º 45
0
        // A "surprising" sign extension is:
        //
        // * a conversion with no cast in source code that goes from a smaller
        //   signed type to a larger signed or unsigned type.
        //
        // * an conversion (with or without a cast) from a smaller
        //   signed type to a larger unsigned type.

        private static ulong FindSurprisingSignExtensionBits(BoundExpression expr)
        {
            if (expr.Kind != BoundKind.Conversion)
            {
                return(0);
            }

            BoundConversion conv = (BoundConversion)expr;
            TypeSymbol      from = conv.Operand.Type;
            TypeSymbol      to   = conv.Type;

            if ((object)from == null || (object)to == null)
            {
                return(0);
            }

            if (from.IsNullableType())
            {
                from = from.GetNullableUnderlyingType();
            }

            if (to.IsNullableType())
            {
                to = to.GetNullableUnderlyingType();
            }

            SpecialType fromSpecialType = from.SpecialType;
            SpecialType toSpecialType   = to.SpecialType;

            if (!fromSpecialType.IsIntegralType() || !toSpecialType.IsIntegralType())
            {
                return(0);
            }

            int fromSize = fromSpecialType.SizeInBytes();
            int toSize   = toSpecialType.SizeInBytes();

            if (fromSize == 0 || toSize == 0)
            {
                return(0);
            }

            // The operand might itself be a conversion, and might be contributing
            // surprising bits. We might have more, fewer or the same surprising bits
            // as the operand.

            ulong recursive = FindSurprisingSignExtensionBits(conv.Operand);

            if (fromSize == toSize)
            {
                // No change.
                return(recursive);
            }

            if (toSize < fromSize)
            {
                // We are casting from a larger type to a smaller type, and are therefore
                // losing surprising bits.
                switch (toSize)
                {
                case 1: return(unchecked ((ulong)(byte)recursive));

                case 2: return(unchecked ((ulong)(ushort)recursive));

                case 4: return(unchecked ((ulong)(uint)recursive));
                }
                Debug.Assert(false, "How did we get here?");
                return(recursive);
            }

            // We are converting from a smaller type to a larger type, and therefore might
            // be adding surprising bits. First of all, the smaller type has got to be signed
            // for there to be sign extension.

            bool fromSigned = fromSpecialType.IsSignedIntegralType();

            if (!fromSigned)
            {
                return(recursive);
            }

            // OK, we know that the "from" type is a signed integer that is smaller than the
            // "to" type, so we are going to have sign extension. Is it surprising? The only
            // time that sign extension is *not* surprising is when we have a cast operator
            // to a *signed* type. That is, (int)myShort is not a surprising sign extension.

            if (conv.ExplicitCastInCode && toSpecialType.IsSignedIntegralType())
            {
                return(recursive);
            }

            // Note that we *could* be somewhat more clever here. Consider the following edge case:
            //
            // (ulong)(int)(uint)(ushort)mySbyte
            //
            // We could reason that the sbyte-to-ushort conversion is going to add one byte of
            // unexpected sign extension. The conversion from ushort to uint adds no more bytes.
            // The conversion from uint to int adds no more bytes. Does the conversion from int
            // to ulong add any more bytes of unexpected sign extension? Well, no, because we
            // know that the previous conversion from ushort to uint will ensure that the top bit
            // of the uint is off!
            //
            // But we are not going to try to be that clever. In the extremely unlikely event that
            // someone does this, we will record that the unexpectedly turned-on bits are
            // 0xFFFFFFFF0000FF00, even though we could in theory deduce that only 0x000000000000FF00
            // are the unexpected bits.

            ulong result = recursive;

            for (int i = fromSize; i < toSize; ++i)
            {
                result |= (0xFFUL) << (i * 8);
            }

            return(result);
        }
Exemplo n.º 46
0
        private BoundExpression ConvertCaseExpression(TypeSymbol switchGoverningType, CSharpSyntaxNode node, BoundExpression caseExpression, Binder sectionBinder, ref ConstantValue constantValueOpt, DiagnosticBag diagnostics, bool isGotoCaseExpr = false)
        {
            BoundExpression convertedCaseExpression;
            if (!isGotoCaseExpr)
            {
                // NOTE: This will allow user-defined conversions, even though they're not allowed here.  This is acceptable
                // because the result of a user-defined conversion does not have a ConstantValue and we'll report a diagnostic
                // to that effect below (same error code as Dev10).
                convertedCaseExpression = sectionBinder.GenerateConversionForAssignment(switchGoverningType, caseExpression, diagnostics);
            }
            else
            {
                // SPEC VIOLATION for Dev10 COMPATIBILITY:

                // Dev10 compiler violates the SPEC comment below:
                //      "if the constant-expression is not implicitly convertible (§6.1) to 
                //      the governing type of the nearest enclosing switch statement, 
                //      a compile-time error occurs"

                // If there is no implicit conversion from gotoCaseExpression to switchGoverningType,
                // but there exists an explicit conversion, Dev10 compiler generates a warning "WRN_GotoCaseShouldConvert"
                // instead of an error. See test "CS0469_NoImplicitConversionWarning".

                // CONSIDER: Should we introduce a breaking change and violate Dev10 compatibility and follow the spec?

                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                Conversion conversion = sectionBinder.Conversions.ClassifyConversionFromExpression(caseExpression, switchGoverningType, ref useSiteDiagnostics);
                diagnostics.Add(node, useSiteDiagnostics);
                if (!conversion.IsValid)
                {
                    GenerateImplicitConversionError(diagnostics, node, conversion, caseExpression, switchGoverningType);
                }
                else if (!conversion.IsImplicit)
                {
                    diagnostics.Add(ErrorCode.WRN_GotoCaseShouldConvert, node.Location, switchGoverningType);
                }

                convertedCaseExpression = sectionBinder.CreateConversion(caseExpression, conversion, switchGoverningType, diagnostics);
            }

            if (switchGoverningType.IsNullableType()
                && convertedCaseExpression.Kind == BoundKind.Conversion
                // Null is a special case here because we want to compare null to the Nullable<T> itself, not to the underlying type.
                && (convertedCaseExpression.ConstantValue == null || !convertedCaseExpression.ConstantValue.IsNull))
            {
                var operand = ((BoundConversion)convertedCaseExpression).Operand;

                // We are not intested in the diagnostic that get created here
                var diagnosticBag = DiagnosticBag.GetInstance();
                constantValueOpt = sectionBinder.CreateConversion(operand, switchGoverningType.GetNullableUnderlyingType(), diagnosticBag).ConstantValue;
                diagnosticBag.Free();
            }
            else
            {
                constantValueOpt = convertedCaseExpression.ConstantValue;
            }

            return convertedCaseExpression;
        }
Exemplo n.º 47
0
        private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTupleLiteral sourceTuple, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
        {
            // We have a successful tuple conversion; rather than producing a separate conversion node 
            // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments.
            Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType());

            var destinationWithoutNullable = destination;
            var conversionWithoutNullable = conversion;

            if (conversion.Kind == ConversionKind.ImplicitNullable)
            {
                destinationWithoutNullable = destination.GetNullableUnderlyingType();
                conversionWithoutNullable = conversion.UnderlyingConversions[0];
            }

            Debug.Assert(conversionWithoutNullable.IsTupleLiteralConversion);

            NamedTypeSymbol targetType = (NamedTypeSymbol)destinationWithoutNullable;
            if (targetType.IsTupleType)
            {
                var destTupleType = (TupleTypeSymbol)targetType;
                // do not lose the original element names in the literal if different from names in the target

                TupleTypeSymbol.ReportNamesMismatchesIfAny(targetType, sourceTuple, diagnostics);

                // Come back to this, what about locations? (https://github.com/dotnet/roslyn/issues/11013)
                targetType = destTupleType.WithElementNames(sourceTuple.ArgumentNamesOpt);
            }

            var arguments = sourceTuple.Arguments;
            var convertedArguments = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length);

            ImmutableArray<TypeSymbol> targetElementTypes = targetType.GetElementTypesOfTupleOrCompatible();
            Debug.Assert(targetElementTypes.Length == arguments.Length, "converting a tuple literal to incompatible type?");
            var underlyingConversions = conversionWithoutNullable.UnderlyingConversions;

            for (int i = 0; i < arguments.Length; i++)
            {
                var argument = arguments[i];
                var destType = targetElementTypes[i];
                var elementConversion = underlyingConversions[i];

                convertedArguments.Add(CreateConversion(argument.Syntax, argument, elementConversion, isCast, destType, diagnostics));
            }

            BoundExpression result = new BoundConvertedTupleLiteral(
                sourceTuple.Syntax,
                sourceTuple.Type,
                convertedArguments.ToImmutableAndFree(),
                targetType);

            if (sourceTuple.Type != destination)
            {
                // literal cast is applied to the literal 
                result = new BoundConversion(
                    sourceTuple.Syntax,
                    result,
                    conversion,
                    @checked: false,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: destination);
            }

            // If we had a cast in the code, keep conversion in the tree.
            // even though the literal is already converted to the target type.
            if (isCast)
            {
                result = new BoundConversion(
                    syntax,
                    result,
                    Conversion.Identity,
                    @checked: false,
                    explicitCastInCode: isCast,
                    constantValueOpt: ConstantValue.NotAvailable,
                    type: destination);
            }

            return result;
        }
Exemplo n.º 48
0
        private void AddUserDefinedConversionsToExplicitCandidateSet(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ArrayBuilder <UserDefinedConversionAnalysis> u,
            NamedTypeSymbol declaringType,
            string operatorName,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);
            Debug.Assert(u != null);
            Debug.Assert((object)declaringType != null);
            Debug.Assert(operatorName != null);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U.
            // SPEC: The set consists of the user-defined and lifted implicit or explicit
            // SPEC: conversion operators declared by the classes and structs in D that convert
            // SPEC: from a type encompassing E or encompassed by S (if it exists) to a type
            // SPEC: encompassing or encompassed by T.

            // DELIBERATE SPEC VIOLATION:
            //
            // The spec here essentially says that we add an applicable "regular" conversion and
            // an applicable lifted conversion, if there is one, to the candidate set, and then
            // let them duke it out to determine which one is "best".
            //
            // This is not at all what the native compiler does, and attempting to implement
            // the specification, or slight variations on it, produces too many backwards-compatibility
            // breaking changes.
            //
            // The native compiler deviates from the specification in two major ways here.
            // First, it does not add *both* the regular and lifted forms to the candidate set.
            // Second, the way it characterizes a "lifted" form is very, very different from
            // how the specification characterizes a lifted form.
            //
            // An operation, in this case, X-->Y, is properly said to be "lifted" to X?-->Y? via
            // the rule that X?-->Y? matches the behavior of X-->Y for non-null X, and converts
            // null X to null Y otherwise.
            //
            // The native compiler, by contrast, takes the existing operator and "lifts" either
            // the operator's parameter type or the operator's return type to nullable. For
            // example, a conversion from X?-->Y would be "lifted" to X?-->Y? by making the
            // conversion from X? to Y, and then from Y to Y?.  No "lifting" semantics
            // are imposed; we do not check to see if the X? is null. This operator is not
            // actually "lifted" at all; rather, an implicit conversion is applied to the
            // output. **The native compiler considers the result type Y? of that standard implicit
            // conversion to be the result type of the "lifted" conversion**, rather than
            // properly considering Y to be the result type of the conversion for the purposes
            // of computing the best output type.
            //
            // Moreover: the native compiler actually *does* implement nullable lifting semantics
            // in the case where the input type of the user-defined conversion is a non-nullable
            // value type and the output type is a nullable value type **or pointer type, or
            // reference type**. This is an enormous departure from the specification; the
            // native compiler will take a user-defined conversion from X-->Y? or X-->C and "lift"
            // it to a conversion from X?-->Y? or X?-->C that has nullable semantics.
            //
            // This is quite confusing. In this code we will classify the conversion as either
            // "normal" or "lifted" on the basis of *whether or not special lifting semantics
            // are to be applied*. That is, whether or not a later rewriting pass is going to
            // need to insert a check to see if the source expression is null, and decide
            // whether or not to call the underlying unlifted conversion or produce a null
            // value without calling the unlifted conversion.
            // DELIBERATE SPEC VIOLATION: See the comment regarding bug 17021 in
            // UserDefinedImplicitConversions.cs.

            if ((object)source != null && source.IsInterfaceType() || target.IsInterfaceType())
            {
                return;
            }

            foreach (MethodSymbol op in declaringType.GetOperators(operatorName))
            {
                // We might have a bad operator and be in an error recovery situation. Ignore it.
                if (op.ReturnsVoid || op.ParameterCount != 1 || op.ReturnType.TypeKind == TypeKind.Error)
                {
                    continue;
                }

                TypeSymbol convertsFrom   = op.GetParameterType(0);
                TypeSymbol convertsTo     = op.ReturnType;
                Conversion fromConversion = EncompassingExplicitConversion(sourceExpression, source, convertsFrom, ref useSiteDiagnostics);
                Conversion toConversion   = EncompassingExplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);

                // We accept candidates for which the parameter type encompasses the *underlying* source type.
                if (!fromConversion.Exists &&
                    (object)source != null &&
                    source.IsNullableType() &&
                    EncompassingExplicitConversion(null, source.GetNullableUnderlyingType(), convertsFrom, ref useSiteDiagnostics).Exists)
                {
                    fromConversion = ClassifyBuiltInConversion(source, convertsFrom, ref useSiteDiagnostics);
                }

                // As in dev11 (and the revised spec), we also accept candidates for which the return type is encompassed by the *stripped* target type.
                if (!toConversion.Exists &&
                    (object)target != null &&
                    target.IsNullableType() &&
                    EncompassingExplicitConversion(null, convertsTo, target.GetNullableUnderlyingType(), ref useSiteDiagnostics).Exists)
                {
                    toConversion = ClassifyBuiltInConversion(convertsTo, target, ref useSiteDiagnostics);
                }

                // In the corresponding implicit conversion code we can get away with first
                // checking to see if standard implicit conversions exist from the source type
                // to the parameter type, and from the return type to the target type. If not,
                // then we can check for a lifted operator.
                //
                // That's not going to cut it in the explicit conversion code. Suppose we have
                // a conversion X-->Y and have source type X? and target type Y?. There *are*
                // standard explicit conversions from X?-->X and Y?-->Y, but we do not want
                // to bind this as an *unlifted* conversion from X? to Y?; we want such a thing
                // to be a *lifted* conversion from X? to Y?, that checks for null on the source
                // and decides to not call the underlying user-defined conversion if it is null.
                //
                // We therefore cannot do what we do in the implicit conversions, where we check
                // to see if the unlifted conversion works, and if it does, then don't add the lifted
                // conversion at all. Rather, we have to see if what we're building here is a
                // lifted conversion or not.
                //
                // Under what circumstances is this conversion a lifted conversion? (In the
                // "spec" sense of a lifted conversion; that is, that we check for null
                // and skip the user-defined conversion if necessary).
                //
                // * The source type must be a nullable value type.
                // * The parameter type must be a non-nullable value type.
                // * The target type must be able to take on a null value.

                if (fromConversion.Exists && toConversion.Exists)
                {
                    if ((object)source != null && source.IsNullableType() && convertsFrom.IsNonNullableValueType() && target.CanBeAssignedNull())
                    {
                        TypeSymbol nullableFrom         = MakeNullableType(convertsFrom);
                        TypeSymbol nullableTo           = convertsTo.IsNonNullableValueType() ? MakeNullableType(convertsTo) : convertsTo;
                        Conversion liftedFromConversion = EncompassingExplicitConversion(sourceExpression, source, nullableFrom, ref useSiteDiagnostics);
                        Conversion liftedToConversion   = EncompassingExplicitConversion(null, nullableTo, target, ref useSiteDiagnostics);
                        Debug.Assert(liftedFromConversion.Exists);
                        Debug.Assert(liftedToConversion.Exists);
                        u.Add(UserDefinedConversionAnalysis.Lifted(op, liftedFromConversion, liftedToConversion, nullableFrom, nullableTo));
                    }
                    else
                    {
                        // There is an additional spec violation in the native compiler. Suppose
                        // we have a conversion from X-->Y and are asked to do "Y? y = new X();"  Clearly
                        // the intention is to convert from X-->Y via the implicit conversion, and then
                        // stick a standard implicit conversion from Y-->Y? on the back end. **In this
                        // situation, the native compiler treats the conversion as though it were
                        // actually X-->Y? in source for the purposes of determining the best target
                        // type of a set of operators.
                        //
                        // Similarly, if we have a conversion from X-->Y and are asked to do
                        // an explicit conversion from X? to Y then we treat the conversion as
                        // though it really were X?-->Y for the purposes of determining the best
                        // source type of a set of operators.
                        //
                        // We perpetuate these fictions here.

                        if (target.IsNullableType() && convertsTo.IsNonNullableValueType())
                        {
                            convertsTo   = MakeNullableType(convertsTo);
                            toConversion = EncompassingExplicitConversion(null, convertsTo, target, ref useSiteDiagnostics);
                        }

                        if ((object)source != null && source.IsNullableType() && convertsFrom.IsNonNullableValueType())
                        {
                            convertsFrom   = MakeNullableType(convertsFrom);
                            fromConversion = EncompassingExplicitConversion(null, convertsFrom, source, ref useSiteDiagnostics);
                        }

                        u.Add(UserDefinedConversionAnalysis.Normal(op, fromConversion, toConversion, convertsFrom, convertsTo));
                    }
                }
            }
        }
        private BoundExpression RewriteNullableConversion(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            bool @checked,
            bool explicitCastInCode,
            TypeSymbol rewrittenType)
        {
            Debug.Assert((object)rewrittenType != null);

            if (inExpressionLambda)
            {
                return RewriteLiftedConversionInExpressionTree(syntax, rewrittenOperand, conversionKind, @checked, explicitCastInCode, rewrittenType);
            }

            TypeSymbol rewrittenOperandType = rewrittenOperand.Type;
            Debug.Assert(rewrittenType.IsNullableType() || rewrittenOperandType.IsNullableType());

            if (rewrittenOperandType.IsNullableType() && rewrittenType.IsNullableType())
            {
                return RewriteFullyLiftedBuiltInConversion(syntax, rewrittenOperand, conversionKind, @checked, rewrittenType);
            }
            else if (rewrittenType.IsNullableType())
            {
                // SPEC: If the nullable conversion is from S to T?, the conversion is 
                // SPEC: evaluated as the underlying conversion from S to T followed
                // SPEC: by a wrapping from T to T?.

                BoundExpression rewrittenConversion = MakeConversion(rewrittenOperand, rewrittenType.GetNullableUnderlyingType(), @checked);
                MethodSymbol ctor = GetNullableMethod(syntax, rewrittenType, SpecialMember.System_Nullable_T__ctor);
                return new BoundObjectCreationExpression(syntax, ctor, rewrittenConversion);
            }
            else
            {
                // SPEC: if the nullable conversion is from S? to T, the conversion is
                // SPEC: evaluated as an unwrapping from S? to S followed by the underlying
                // SPEC: conversion from S to T.

                // We can do a simple optimization here if we know that the source is never null:

                BoundExpression nonNullValue = NullableAlwaysHasValue(rewrittenOperand);
                if (nonNullValue != null)
                {
                    return MakeConversion(nonNullValue, rewrittenType, @checked);
                }

                // (If the source is known to be null then we need to keep the call to get Value 
                // in place so that it throws at runtime.)

                MethodSymbol get_Value = GetNullableMethod(syntax, rewrittenOperandType, SpecialMember.System_Nullable_T_get_Value);
                return MakeConversion(BoundCall.Synthesized(syntax, rewrittenOperand, get_Value), rewrittenType, @checked);
            }
        }
Exemplo n.º 50
0
 public static bool CanBeAssignedNull(this TypeSymbol type)
 {
     return(type.IsReferenceType || type.IsPointerType() || type.IsNullableType());
 }
Exemplo n.º 51
0
        private static BinaryOperatorKind GetCorrespondingBinaryOperator(BoundIncrementOperator node)
        {
            // We need to create expressions that have the semantics of incrementing or decrementing:
            // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal and
            // any enum.  However, the binary addition operators we have at our disposal are just
            // int, uint, long, ulong, float, double and decimal.

            UnaryOperatorKind  unaryOperatorKind = node.OperatorKind;
            BinaryOperatorKind result;

            switch (unaryOperatorKind.OperandTypes())
            {
            case UnaryOperatorKind.Int:
            case UnaryOperatorKind.SByte:
            case UnaryOperatorKind.Short:
                result = BinaryOperatorKind.Int;
                break;

            case UnaryOperatorKind.Byte:
            case UnaryOperatorKind.UShort:
            case UnaryOperatorKind.Char:
            case UnaryOperatorKind.UInt:
                result = BinaryOperatorKind.UInt;
                break;

            case UnaryOperatorKind.Long:
                result = BinaryOperatorKind.Long;
                break;

            case UnaryOperatorKind.ULong:
                result = BinaryOperatorKind.ULong;
                break;

            case UnaryOperatorKind.Float:
                result = BinaryOperatorKind.Float;
                break;

            case UnaryOperatorKind.Double:
                result = BinaryOperatorKind.Double;
                break;

            case UnaryOperatorKind.Decimal:     //Dev10 special cased this, but we'll let DecimalRewriter handle it
                result = BinaryOperatorKind.Decimal;
                break;

            case UnaryOperatorKind.Enum:
            {
                TypeSymbol underlyingType = node.Type;
                if (underlyingType.IsNullableType())
                {
                    underlyingType = underlyingType.GetNullableUnderlyingType();
                }
                Debug.Assert(underlyingType.IsEnumType());
                underlyingType = underlyingType.GetEnumUnderlyingType();

                // Operator overload resolution will not have chosen the enumerated type
                // unless the operand actually is of the enumerated type (or nullable enum type.)

                switch (underlyingType.SpecialType)
                {
                case SpecialType.System_SByte:
                case SpecialType.System_Int16:
                case SpecialType.System_Int32:
                    result = BinaryOperatorKind.Int;
                    break;

                case SpecialType.System_Byte:
                case SpecialType.System_UInt16:
                case SpecialType.System_UInt32:
                    result = BinaryOperatorKind.UInt;
                    break;

                case SpecialType.System_Int64:
                    result = BinaryOperatorKind.Long;
                    break;

                case SpecialType.System_UInt64:
                    result = BinaryOperatorKind.ULong;
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(underlyingType.SpecialType);
                }
            }
            break;

            case UnaryOperatorKind.Pointer:
                result = BinaryOperatorKind.PointerAndInt;
                break;

            case UnaryOperatorKind.UserDefined:
            case UnaryOperatorKind.Bool:
            default:
                throw ExceptionUtilities.UnexpectedValue(unaryOperatorKind.OperandTypes());
            }

            switch (result)
            {
            case BinaryOperatorKind.UInt:
            case BinaryOperatorKind.Int:
            case BinaryOperatorKind.ULong:
            case BinaryOperatorKind.Long:
            case BinaryOperatorKind.PointerAndInt:
                result |= (BinaryOperatorKind)unaryOperatorKind.OverflowChecks();
                break;
            }

            if (unaryOperatorKind.IsLifted())
            {
                result |= BinaryOperatorKind.Lifted;
            }

            return(result);
        }
        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);
            }
        }
        private static bool HasImplicitEnumerationConversion(BoundExpression source, TypeSymbol destination)
        {
            Debug.Assert((object)source != null);
            Debug.Assert((object)destination != null);

            // SPEC: An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type 
            // SPEC: and to any nullable-type whose underlying type is an enum-type. 
            //
            // For historical reasons we actually allow a conversion from any *numeric constant
            // zero* to be converted to any enum type, not just the literal integer zero.

            bool validType = destination.IsEnumType() ||
                destination.IsNullableType() && destination.GetNullableUnderlyingType().IsEnumType();

            if (!validType)
            {
                return false;
            }

            var sourceConstantValue = source.ConstantValue;
            return sourceConstantValue != null &&
                IsNumericType(source.Type.GetSpecialTypeSafe()) &&
                IsConstantNumericZero(sourceConstantValue);
        }
        private BoundExpression MakeLiftedUserDefinedConversionConsequence(BoundCall call, TypeSymbol resultType)
        {
            if (call.Method.ReturnType.IsNonNullableValueType())
            {
                Debug.Assert(resultType.IsNullableType() && resultType.GetNullableUnderlyingType() == call.Method.ReturnType);
                MethodSymbol ctor = GetNullableMethod(call.Syntax, resultType, SpecialMember.System_Nullable_T__ctor);
                return new BoundObjectCreationExpression(call.Syntax, ctor, call);
            }

            return call;
        }