Exemple #1
0
 internal void Parse(BoundAsOperator boundAsOperator)
 {
     base.Parse(boundAsOperator);
     this.ConversionKind = boundAsOperator.Conversion.Kind;
     this.TargetType     = Deserialize(boundAsOperator.TargetType) as TypeExpression;
     this.Operand        = Deserialize(boundAsOperator.Operand) as Expression;
 }
Exemple #2
0
        public static bool IsImplicitConversion(this ConversionKind kind)
        {
            switch (kind)
            {
            case ConversionKind.AnonymousFunction:
            case ConversionKind.Boxing:
            case ConversionKind.Dynamic:
            case ConversionKind.Identity:
            case ConversionKind.ImplicitConstant:
            case ConversionKind.ImplicitEnumeration:
            case ConversionKind.ImplicitNullable:
            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ImplicitReference:
            case ConversionKind.ImplicitUserDefined:
            case ConversionKind.MethodGroup:
            case ConversionKind.NullLiteral:
                return(true);

            case ConversionKind.ExplicitEnumeration:
            case ConversionKind.ExplicitNullable:
            case ConversionKind.ExplicitNumeric:
            case ConversionKind.ExplicitReference:
            case ConversionKind.ExplicitUserDefined:
            case ConversionKind.NoConversion:
            case ConversionKind.Unboxing:
                return(false);

            default:
                Debug.Fail("Bad conversion kind!");
                return(false);
            }
        }
Exemple #3
0
        internal void Parse(BoundConversion boundConversion)
        {
            base.Parse(boundConversion);
            this.TypeSource     = boundConversion.Operand.Type;
            this.Operand        = Deserialize(boundConversion.Operand) as Expression;
            this.ConversionKind = boundConversion.ConversionKind;
            this.Checked        = boundConversion.Checked;

            var methodGroupOperand = this.Operand as MethodGroup;

            if (methodGroupOperand != null && methodGroupOperand.Method == null)
            {
                var boundMethodGroup = boundConversion.Operand as BoundMethodGroup;
                if (boundMethodGroup != null)
                {
                    var methodGroup = new MethodGroup
                    {
                        ReceiverOpt = Deserialize(boundMethodGroup.ReceiverOpt) as Expression,
                        Method      = boundConversion.ExpressionSymbol as IMethodSymbol
                    };

                    this.Operand = methodGroup;
                }
            }
        }
Exemple #4
0
        private BoundExpression MakeIsOperator(
            BoundIsOperator oldNode,
            SyntaxNode syntax,
            BoundExpression rewrittenOperand,
            BoundTypeExpression rewrittenTargetType,
            ConversionKind conversionKind,
            TypeSymbol rewrittenType)
        {
            if (rewrittenOperand.Kind == BoundKind.MethodGroup)
            {
                var             methodGroup = (BoundMethodGroup)rewrittenOperand;
                BoundExpression?receiver    = methodGroup.ReceiverOpt;
                if (receiver != null && receiver.Kind != BoundKind.ThisReference)
                {
                    // possible side-effect
                    return(RewriteConstantIsOperator(receiver.Syntax, receiver, ConstantValue.False, rewrittenType));
                }
                else
                {
                    return(MakeLiteral(syntax, ConstantValue.False, rewrittenType));
                }
            }

            var operandType = rewrittenOperand.Type;
            var targetType  = rewrittenTargetType.Type;

            Debug.Assert(operandType is { } || rewrittenOperand.ConstantValue !.IsNull);
        /// <summary>
        /// Emits expression and converts it to required type.
        /// </summary>
        public void EmitConvert(BoundExpression expr, TypeSymbol to, ConversionKind conversion = ConversionKind.Implicit, bool notNull = false)
        {
            Debug.Assert(expr != null);
            Debug.Assert(to != null);

            // pop effectively
            if (to.IsVoid())
            {
                expr.Access = BoundAccess.None;

                if (!expr.IsConstant())
                {
                    // POP LOAD <expr>
                    EmitPop(Emit(expr));
                }

                return;
            }

            // bind target expression type
            expr.Access = expr.Access.WithRead(to);

            if (!expr.Access.IsReadRef)
            {
                // constants
                if (expr.ConstantValue.HasValue && to != null)
                {
                    // TODO: ConversionKind.Strict ?
                    EmitConvert(EmitLoadConstant(expr.ConstantValue.Value, to, notNull), 0, to);
                    return;
                }

                // loads value from place most effectively without runtime type checking
                var place = PlaceOrNull(expr);
                if (place != null && place.Type != to)
                {
                    var type = TryEmitVariableSpecialize(place, expr.TypeRefMask);
                    if (type != null)
                    {
                        EmitConvert(type, 0, to);
                        return;
                    }
                }

                // avoiding of load of full value
                if (place != null && place.HasAddress && place.Type != null && place.Type.IsValueType)
                {
                    var conv = DeclaringCompilation.Conversions.ClassifyConversion(place.Type, to, conversion);
                    if (conv.Exists && conv.IsUserDefined && !conv.MethodSymbol.IsStatic)
                    {
                        // (ADDR expr).Method()
                        this.EmitImplicitConversion(EmitCall(ILOpCode.Call, (MethodSymbol)conv.MethodSymbol, expr, ImmutableArray <BoundArgument> .Empty), to, @checked: true);
                        return;
                    }
                }
            }

            //
            EmitConvert(expr.Emit(this), expr.TypeRefMask, to, conversion);
        }
Exemple #6
0
 private Conversion(
     ConversionKind kind,
     UncommonData uncommonData)
 {
     _kind         = kind;
     _uncommonData = uncommonData;
 }
Exemple #7
0
        internal Conversion(ConversionKind kind, DeconstructionInfo deconstructionInfo, ImmutableArray <Conversion> nestedConversions)
        {
            Debug.Assert(kind == ConversionKind.Deconstruction);

            this._kind    = kind;
            _uncommonData = new DeconstructionUncommonData(deconstructionInfo, nestedConversions);
        }
Exemple #8
0
 internal void Parse(BoundNullCoalescingOperator boundNullCoalescingOperator)
 {
     base.Parse(boundNullCoalescingOperator);
     this.LeftOperand    = Deserialize(boundNullCoalescingOperator.LeftOperand) as Expression;
     this.RightOperand   = Deserialize(boundNullCoalescingOperator.RightOperand) as Expression;
     this.ConversionKind = boundNullCoalescingOperator.LeftConversion.Kind;
 }
Exemple #9
0
        private static void AssertTrivialConversion(ConversionKind kind)
        {
            bool isTrivial;

            switch (kind)
            {
            case ConversionKind.NoConversion:
            case ConversionKind.Identity:
            case ConversionKind.ImplicitConstant:
            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ImplicitReference:
            case ConversionKind.ImplicitEnumeration:
            case ConversionKind.ImplicitThrow:
            case ConversionKind.AnonymousFunction:
            case ConversionKind.DefaultOrNullLiteral:
            case ConversionKind.NullToPointer:
            case ConversionKind.PointerToVoid:
            case ConversionKind.PointerToPointer:
            case ConversionKind.PointerToInteger:
            case ConversionKind.IntegerToPointer:
            case ConversionKind.ExplicitReference:
            case ConversionKind.IntPtr:
            case ConversionKind.ExplicitEnumeration:
            case ConversionKind.ExplicitNumeric:
            case ConversionKind.InterpolatedString:
                isTrivial = true;
                break;

            default:
                isTrivial = false;
                break;
            }

            Debug.Assert(isTrivial, "this conversion needs additional data: " + kind);
        }
Exemple #10
0
        private static bool IsEncompassingImplicitConversionKind(ConversionKind kind)
        {
            switch (kind)
            {
            // Doesn't even exist.
            case ConversionKind.NoConversion:

            // These are conversions from expression and do not apply.
            // Specifically disallowed because there would be subtle consequences for the overload betterness rules.
            case ConversionKind.ImplicitDynamic:
            case ConversionKind.MethodGroup:
            case ConversionKind.AnonymousFunction:
            case ConversionKind.InterpolatedString:
            case ConversionKind.SwitchExpression:
            case ConversionKind.ImplicitEnumeration:
            case ConversionKind.StackAllocToPointerType:
            case ConversionKind.StackAllocToSpanType:

            // Not "standard".
            case ConversionKind.ImplicitUserDefined:
            case ConversionKind.ExplicitUserDefined:

            // Not implicit.
            case ConversionKind.ExplicitNumeric:
            case ConversionKind.ExplicitEnumeration:
            case ConversionKind.ExplicitNullable:
            case ConversionKind.ExplicitReference:
            case ConversionKind.Unboxing:
            case ConversionKind.ExplicitDynamic:
            case ConversionKind.PointerToPointer:
            case ConversionKind.PointerToInteger:
            case ConversionKind.IntegerToPointer:
            case ConversionKind.IntPtr:
            case ConversionKind.ExplicitTupleLiteral:
            case ConversionKind.ExplicitTuple:
                return(false);

            // Spec'd in C# 4.
            case ConversionKind.Identity:
            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ImplicitNullable:
            case ConversionKind.ImplicitReference:
            case ConversionKind.Boxing:
            case ConversionKind.ImplicitConstant:
            case ConversionKind.PointerToVoid:

            // Added to spec in Roslyn timeframe.
            case ConversionKind.DefaultOrNullLiteral:     // updated to include "default" in C# 7.1
            case ConversionKind.NullToPointer:

            // Added for C# 7.
            case ConversionKind.ImplicitTupleLiteral:
            case ConversionKind.ImplicitTuple:
            case ConversionKind.ImplicitThrow:
                return(true);

            default:
                throw ExceptionUtilities.UnexpectedValue(kind);
            }
        }
Exemple #11
0
 internal Conversion(UserDefinedConversionResult conversionResult, bool isImplicit)
     : this()
 {
     this.Kind = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators
         ? ConversionKind.NoConversion
         : isImplicit?ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined;
     this.conversionResult = conversionResult;
 }
Exemple #12
0
        private static bool IsEncompassingImplicitConversionKind(ConversionKind kind)
        {
            switch (kind)
            {
            // Doesn't even exist.
            case ConversionKind.NoConversion:

            // Specifically disallowed because there would be subtle
            // consequences for the overload betterness rules.
            case ConversionKind.MethodGroup:
            case ConversionKind.AnonymousFunction:
            case ConversionKind.ImplicitDynamic:
            case ConversionKind.InterpolatedString:

            // DELIBERATE SPEC VIOLATION:
            // We do not support an encompassing implicit conversion from a zero constant
            // to an enum type, because the native compiler did not.  It would be a breaking
            // change.
            case ConversionKind.ImplicitEnumeration:

            // Not built in.
            case ConversionKind.ImplicitUserDefined:
            case ConversionKind.ExplicitUserDefined:

            // Not implicit.
            case ConversionKind.ExplicitNumeric:
            case ConversionKind.ExplicitEnumeration:
            case ConversionKind.ExplicitNullable:
            case ConversionKind.ExplicitReference:
            case ConversionKind.Unboxing:
            case ConversionKind.ExplicitDynamic:
            case ConversionKind.PointerToPointer:
            case ConversionKind.PointerToInteger:
            case ConversionKind.IntegerToPointer:
            case ConversionKind.IntPtr:
                return(false);

            // Spec'd in C# 4.
            case ConversionKind.Identity:
            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ImplicitNullable:
            case ConversionKind.ImplicitReference:
            case ConversionKind.Boxing:
            case ConversionKind.ImplicitConstant:
            case ConversionKind.PointerToVoid:

            // Added to spec in Roslyn timeframe.
            case ConversionKind.NullLiteral:
            case ConversionKind.NullToPointer:

            // Added for C# 7.
            case ConversionKind.ImplicitTuple:
                return(true);

            default:
                throw ExceptionUtilities.UnexpectedValue(kind);
            }
        }
Exemple #13
0
        // Is the particular conversion an implicit conversion?
        public static bool IsImplicitConversion(this ConversionKind conversionKind)
        {
            switch (conversionKind)
            {
            case NoConversion:
            case UnsetConversionKind:
                return(false);

            case Identity:
            case ImplicitNumeric:
            case ImplicitTupleLiteral:
            case ImplicitTuple:
            case ImplicitEnumeration:
            case ImplicitThrow:
            case ImplicitNullable:
            case NullLiteral:
            case DefaultLiteral:
            case ImplicitReference:
            case Boxing:
            case ImplicitDynamic:
            case ImplicitConstant:
            case ImplicitUtf8StringLiteral:
            case ImplicitUserDefined:
            case AnonymousFunction:
            case ConversionKind.MethodGroup:
            case ConversionKind.FunctionType:
            case ImplicitPointerToVoid:
            case ImplicitNullToPointer:
            case InterpolatedString:
            case InterpolatedStringHandler:
            case SwitchExpression:
            case ConditionalExpression:
            case Deconstruction:
            case StackAllocToPointerType:
            case StackAllocToSpanType:
            case ImplicitPointer:
            case ObjectCreation:
                return(true);

            case ExplicitNumeric:
            case ExplicitTuple:
            case ExplicitTupleLiteral:
            case ExplicitEnumeration:
            case ExplicitNullable:
            case ExplicitReference:
            case Unboxing:
            case ExplicitDynamic:
            case ExplicitUserDefined:
            case ExplicitPointerToPointer:
            case ExplicitPointerToInteger:
            case ExplicitIntegerToPointer:
            case IntPtr:
                return(false);

            default:
                throw ExceptionUtilities.UnexpectedValue(conversionKind);
            }
        }
Exemple #14
0
 internal CommonConversion(bool exists, bool isIdentity, bool isNumeric, bool isReference, bool isImplicit, IMethodSymbol methodSymbol)
 {
     _conversionKind = (exists ? ConversionKind.Exists : ConversionKind.None) |
                       (isIdentity ? ConversionKind.IsIdentity : ConversionKind.None) |
                       (isNumeric ? ConversionKind.IsNumeric : ConversionKind.None) |
                       (isReference ? ConversionKind.IsReference : ConversionKind.None) |
                       (isImplicit ? ConversionKind.IsImplicit : ConversionKind.None);
     MethodSymbol = methodSymbol;
 }
Exemple #15
0
 public Conv(ILInstruction argument, PrimitiveType targetType, bool checkForOverflow, Sign inputSign) : base(OpCode.Conv, argument)
 {
     this.TargetType       = targetType;
     this.CheckForOverflow = checkForOverflow;
     this.InputSign        = inputSign;
     Debug.Assert((inputSign != Sign.None) == (checkForOverflow || targetType == PrimitiveType.R4 || targetType == PrimitiveType.R8));
     this.Kind = GetConversionKind(targetType, argument.ResultType, inputSign);
     Debug.Assert(this.Kind != ConversionKind.Invalid);
 }
        // Is the particular conversion an implicit conversion?
        public static bool IsImplicitConversion(this ConversionKind conversionKind)
        {
            switch (conversionKind)
            {
            case ConversionKind.NoConversion:
                return(false);

            case ConversionKind.Identity:
            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ImplicitTupleLiteral:
            case ConversionKind.ImplicitTuple:
            case ConversionKind.ImplicitEnumeration:
            case ConversionKind.ImplicitThrow:
            case ConversionKind.ImplicitNullable:
            case ConversionKind.DefaultOrNullLiteral:
            case ConversionKind.ImplicitReference:
            case ConversionKind.Boxing:
            case ConversionKind.ImplicitDynamic:
            case ConversionKind.ImplicitConstant:
            case ConversionKind.ImplicitUserDefined:
            case ConversionKind.AnonymousFunction:
            case ConversionKind.MethodGroup:
            case ConversionKind.PointerToVoid:
            case ConversionKind.NullToPointer:
            case ConversionKind.InterpolatedString:
            case ConversionKind.Deconstruction:
            case ConversionKind.StackAllocToPointerType:
            case ConversionKind.StackAllocToSpanType:

#if XSHARP
            case ConversionKind.IntegerToPointer:
            case ConversionKind.IntPtr:
#endif
                return(true);

            case ConversionKind.ExplicitNumeric:
            case ConversionKind.ExplicitTuple:
            case ConversionKind.ExplicitTupleLiteral:
            case ConversionKind.ExplicitEnumeration:
            case ConversionKind.ExplicitNullable:
            case ConversionKind.ExplicitReference:
            case ConversionKind.Unboxing:
            case ConversionKind.ExplicitDynamic:
            case ConversionKind.ExplicitUserDefined:
            case ConversionKind.PointerToPointer:
            case ConversionKind.PointerToInteger:
#if !XSHARP
            case ConversionKind.IntegerToPointer:
            case ConversionKind.IntPtr:
#endif
                return(false);

            default:
                throw ExceptionUtilities.UnexpectedValue(conversionKind);
            }
        }
Exemple #17
0
 internal Conversion(ConversionKind kind, ImmutableArray <Conversion> nestedConversions)
 {
     this._kind    = kind;
     _uncommonData = new UncommonData(
         isExtensionMethod: false,
         isArrayIndex: false,
         conversionResult: default(UserDefinedConversionResult),
         conversionMethod: null,
         nestedConversions: nestedConversions);
 }
Exemple #18
0
 // For the method group, lambda and anonymous method conversions
 internal Conversion(ConversionKind kind, MethodSymbol methodGroupConversionMethod, bool isExtensionMethod)
     : this()
 {
     this.Kind = kind;
     this.methodGroupConversionMethod = methodGroupConversionMethod;
     if (isExtensionMethod)
     {
         this.flags = IsExtensionMethodMask;
     }
 }
Exemple #19
0
 // For the method group, lambda and anonymous method conversions
 internal Conversion(ConversionKind kind, MethodSymbol conversionMethod, bool isExtensionMethod)
 {
     this._kind    = kind;
     _uncommonData = new UncommonData(
         isExtensionMethod: isExtensionMethod,
         isArrayIndex: false,
         conversionResult: default(UserDefinedConversionResult),
         conversionMethod: conversionMethod,
         nestedConversions: default(ImmutableArray <Conversion>));
 }
Exemple #20
0
 public override ILInstruction UnwrapConv(ConversionKind kind)
 {
     if (this.Kind == kind)
     {
         return(Argument.UnwrapConv(kind));
     }
     else
     {
         return(this);
     }
 }
Exemple #21
0
 internal CommonConversion(bool exists, bool isIdentity, bool isNumeric, bool isReference, bool isImplicit, bool isNullable, IMethodSymbol?methodSymbol, ITypeSymbol?constrainedToType)
 {
     _conversionKind = (exists ? ConversionKind.Exists : ConversionKind.None) |
                       (isIdentity ? ConversionKind.IsIdentity : ConversionKind.None) |
                       (isNumeric ? ConversionKind.IsNumeric : ConversionKind.None) |
                       (isReference ? ConversionKind.IsReference : ConversionKind.None) |
                       (isImplicit ? ConversionKind.IsImplicit : ConversionKind.None) |
                       (isNullable ? ConversionKind.IsNullable : ConversionKind.None);
     MethodSymbol      = methodSymbol;
     ConstrainedToType = constrainedToType;
 }
Exemple #22
0
        internal Conversion(UserDefinedConversionResult conversionResult, bool isImplicit)
        {
            _kind = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators
                ? ConversionKind.NoConversion
                : isImplicit ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined;

            _uncommonData = new UncommonData(
                isExtensionMethod: false,
                isArrayIndex: false,
                conversionResult: conversionResult,
                conversionMethod: null,
                nestedConversions: default);
Exemple #23
0
        public OptionalValue <Series <K, R> > TryAs <R>(ConversionKind conversionKind)
        {
            OptionalValue <IVector <R> >             optionalValue1 = VectorHelpers.tryConvertType <R>(conversionKind, (IVector)this.vector);
            FSharpFunc <IVector <R>, Series <K, R> > fsharpFunc     = (FSharpFunc <IVector <R>, Series <K, R> >) new \u0024Series.TryAs\u00401255 <K, R> (this);
            OptionalValue <IVector <R> >             optionalValue2 = optionalValue1;

            if (optionalValue2.HasValue)
            {
                return(new OptionalValue <Series <K, R> >(fsharpFunc.Invoke(optionalValue2.Value)));
            }
            return(OptionalValue <Series <K, R> > .Missing);
        }
Exemple #24
0
        public OptionalValue <R> TryGetAs <R>(K column, ConversionKind conversionKind)
        {
            var optionalValue1          = this.TryGet(column);
            Func <object, R> fsharpFunc = (FSharpFunc <object, R>) new \u0024Series.TryGetAs\u00401248\u002D1 <R> (conversionKind);
            var optionalValue2          = optionalValue1;

            if (optionalValue2.HasValue)
            {
                return(new OptionalValue <R>(fsharpFunc.Invoke(optionalValue2.Value)));
            }
            return(OptionalValue <R> .Missing);
        }
Exemple #25
0
        // Is the particular conversion a used-defined conversion?
        public static bool IsUserDefinedConversion(this ConversionKind conversionKind)
        {
            switch (conversionKind)
            {
            case ImplicitUserDefined:
            case ExplicitUserDefined:
                return(true);

            default:
                return(false);
            }
        }
Exemple #26
0
        private Conversion(ConversionKind kind, bool isExtensionMethod, bool isArrayIndex, UserDefinedConversionResult conversionResult, MethodSymbol methodGroupConversionMethod)
        {
            this.Kind                        = kind;
            this.conversionResult            = conversionResult;
            this.methodGroupConversionMethod = methodGroupConversionMethod;

            this.flags = isExtensionMethod ? IsExtensionMethodMask : (byte)0;
            if (isArrayIndex)
            {
                flags |= IsArrayIndexMask;
            }
        }
Exemple #27
0
        public Conv(ILInstruction argument, StackType inputType, Sign inputSign, PrimitiveType targetType, bool checkForOverflow, bool isLifted = false)
            : base(OpCode.Conv, argument)
        {
            bool needsSign = checkForOverflow || targetType == PrimitiveType.R4 || targetType == PrimitiveType.R8;

            Debug.Assert(!(needsSign && inputSign == Sign.None));
            this.InputType        = inputType;
            this.InputSign        = needsSign ? inputSign : Sign.None;
            this.TargetType       = targetType;
            this.CheckForOverflow = checkForOverflow;
            this.Kind             = GetConversionKind(targetType, this.InputType, this.InputSign);
            this.IsLifted         = isLifted;
        }
Exemple #28
0
        internal void Parse(BoundConversion boundConversion)
        {
            base.Parse(boundConversion);
            this.TypeSource     = boundConversion.Operand.Type;
            this.Operand        = Deserialize(boundConversion.Operand) as Expression;
            this.ConversionKind = boundConversion.ConversionKind;

            ////var castExpression = boundConversion.Syntax.Green as CastExpressionSyntax;
            ////if (castExpression != null)
            ////{
            ////    // TODO: finish it
            ////}
        }
Exemple #29
0
        public Conv(ILInstruction argument, StackType inputType, Sign inputSign, PrimitiveType targetType, bool checkForOverflow, bool isLifted = false)
            : base(OpCode.Conv, argument)
        {
            bool needsSign = checkForOverflow || (!inputType.IsFloatType() && (targetType == PrimitiveType.R4 || targetType == PrimitiveType.R8));

            Debug.Assert(!(needsSign && inputSign == Sign.None));
            this.InputSign        = needsSign ? inputSign : Sign.None;
            this.InputType        = inputType;
            this.TargetType       = targetType;
            this.CheckForOverflow = checkForOverflow;
            this.Kind             = GetConversionKind(targetType, this.InputType, this.InputSign);
            // Debug.Assert(Kind != ConversionKind.Invalid); // invalid conversion can happen with invalid IL/missing references
            this.IsLifted = isLifted;
        }
Exemple #30
0
        public static bool IsPointerConversion(this ConversionKind kind)
        {
            switch (kind)
            {
            case ConversionKind.PointerToVoid:
            case ConversionKind.PointerToPointer:
            case ConversionKind.PointerToInteger:
            case ConversionKind.IntegerToPointer:
            case ConversionKind.NullToPointer:
                return(true);

            default:
                return(false);
            }
        }
        private void TestClassifyConversionBuiltInNumeric(string from, string to, ConversionKind ck)
        {
            const string template = @"
class C
{{
    static void Foo({1} v) {{ }}

    static void Main() {{ {0} v = default({0}); Foo({2}v); }}
}}
";

            var isExplicitConversion = ck == ConversionKind.ExplicitNumeric;
            var source = string.Format(template, from, to, isExplicitConversion ? "(" + to + ")" : "");
            var tree = Parse(source);
            var comp = CreateCompilationWithMscorlib(tree);
            comp.VerifyDiagnostics();

            var model = comp.GetSemanticModel(tree);
            var c = (TypeDeclarationSyntax)tree.GetCompilationUnitRoot().Members[0];
            var main = (MethodDeclarationSyntax)c.Members[1];
            var call = (InvocationExpressionSyntax)((ExpressionStatementSyntax)main.Body.Statements[1]).Expression;
            var arg = call.ArgumentList.Arguments[0].Expression;

            if (isExplicitConversion)
            {
                ConversionTestHelper(model, ((CastExpressionSyntax)arg).Expression, model.GetTypeInfo(arg).ConvertedType, ck);
            }
            else
            {
                ConversionTestHelper(model, arg, ck, ck);
            }
        }
 public BoundExpression Convert(TypeSymbol type, BoundExpression arg, ConversionKind conversionKind, bool isChecked = false)
 {
     return new BoundConversion(
         Syntax,
         arg,
         conversionKind,
         LookupResultKind.Viable,
         isBaseConversion: false,
         symbolOpt: null,
         @checked: isChecked,
         explicitCastInCode: true,
         isExtensionMethod: false,
         isArrayIndex: false,
         constantValueOpt: null,
         type: type)
         { WasCompilerGenerated = true };
 }
        public void ClassifyConversionBuiltInNumeric()
        {
            const ConversionKind ID = ConversionKind.Identity;
            const ConversionKind IN = ConversionKind.ImplicitNumeric;
            const ConversionKind XN = ConversionKind.ExplicitNumeric;

            var types = new[] { "sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "char", "float", "double", "decimal" };
            var conversions = new ConversionKind[,] {
                // to     sb  b  s  us i ui  l ul  c  f  d  m
                // from
                /* sb */ { ID, XN, IN, XN, IN, XN, IN, XN, XN, IN, IN, IN },
                /*  b */ { XN, ID, IN, IN, IN, IN, IN, IN, XN, IN, IN, IN },
                /*  s */ { XN, XN, ID, XN, IN, XN, IN, XN, XN, IN, IN, IN },
                /* us */ { XN, XN, XN, ID, IN, IN, IN, IN, XN, IN, IN, IN },
                /*  i */ { XN, XN, XN, XN, ID, XN, IN, XN, XN, IN, IN, IN },
                /* ui */ { XN, XN, XN, XN, XN, ID, IN, IN, XN, IN, IN, IN },
                /*  l */ { XN, XN, XN, XN, XN, XN, ID, XN, XN, IN, IN, IN },
                /* ul */ { XN, XN, XN, XN, XN, XN, XN, ID, XN, IN, IN, IN },
                /*  c */ { XN, XN, XN, IN, IN, IN, IN, IN, ID, IN, IN, IN },
                /*  f */ { XN, XN, XN, XN, XN, XN, XN, XN, XN, ID, IN, XN },
                /*  d */ { XN, XN, XN, XN, XN, XN, XN, XN, XN, XN, ID, XN },
                /*  m */ { XN, XN, XN, XN, XN, XN, XN, XN, XN, XN, XN, ID }
            };

            for (var from = 0; from < types.Length; from++)
            {
                for (var to = 0; to < types.Length; to++)
                {
                    TestClassifyConversionBuiltInNumeric(types[from], types[to], conversions[from, to]);
                }
            }
        }
Exemple #34
0
        internal static ConstantValue GetAsOperatorConstantResult(TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue)
        {
            // NOTE:    Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue
            // NOTE:    (they are non-constant expressions according to Section 7.19 of the specification),
            // NOTE:    we want to perform constant analysis of is/as expressions during binding to generate warnings (always true/false/null)
            // NOTE:    and during rewriting for optimized codegen.

            ConstantValue isOperatorConstantResult = GetIsOperatorConstantResult(operandType, targetType, conversionKind, operandConstantValue);
            if (isOperatorConstantResult != null && !isOperatorConstantResult.BooleanValue)
            {
                return ConstantValue.Null;
            }

            return null;
        }
        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);
            }
        }
        internal static ConstantValue GetAsOperatorConstantResult(TypeSymbol operandType, TypeSymbol targetType, ConversionKind conversionKind, ConstantValue operandConstantValue)
        {
            // NOTE:    Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue
            // NOTE:    (they are non-constant expressions according to Section 7.19 of the specification),
            // NOTE:    we want to perform constant analysis of is/as expressions during binding to generate warnings (always true/false/null)
            // NOTE:    and during rewriting for optimized codegen.

            // Native compiler port:
            //     // check for case we know is always false
            //     if (arg->isNull() || !canCast(arg, type2, NOUDC) && type1->IsValType() && type2->isClassType() && (!type1->IsTypeParameterType() || !type2->isPredefType(PT_ENUM)))
            //     {
            //          GetErrorContext()->Error(tree, WRN_AlwaysNull, type2);
            //          return rval;
            //     }

            if (operandConstantValue == ConstantValue.Null ||
                (conversionKind == ConversionKind.NoConversion &&
                 (operandType.IsValueType && targetType.IsClassType() && (!operandType.IsTypeParameter() || targetType.SpecialType != SpecialType.System_Enum))))
            {
                return ConstantValue.Null;
            }
            else
            {
                return null;
            }
        }
        private BoundExpression RewriteFullyLiftedBuiltInConversion(
            CSharpSyntaxNode syntax,
            BoundExpression operand,
            ConversionKind kind,
            bool @checked,
            TypeSymbol type)
        {
            // SPEC: If the nullable conversion is from S? to T?:
            // SPEC: * If the source HasValue property is false the result
            // SPEC:   is a null value of type T?.
            // SPEC: * Otherwise the conversion is evaluated as an unwrapping
            // SPEC:   from S? to S, followed by the underlying conversion from
            // SPEC:   S to T, followed by a wrapping from T to T?

            BoundExpression optimized = OptimizeLiftedBuiltInConversion(syntax, operand, kind, @checked, type);
            if (optimized != null)
            {
                return optimized;
            }

            // We are unable to optimize the conversion. "(T?)s" is generated as:
            // S? temp = s;
            // temp.HasValue ? new T?((T)temp.GetValueOrDefault()) : default(T?)

            BoundAssignmentOperator tempAssignment;
            var boundTemp = factory.StoreToTemp(operand, out tempAssignment);
            MethodSymbol get_HasValue = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_get_HasValue);
            MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault);
            BoundExpression condition = BoundCall.Synthesized(syntax, boundTemp, get_HasValue);
            BoundExpression consequence = new BoundObjectCreationExpression(
                syntax,
                GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor),
                MakeConversion(
                    BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault),
                    type.GetNullableUnderlyingType(),
                    @checked));
            BoundExpression alternative = new BoundDefaultOperator(syntax, null, type);
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: condition,
                rewrittenConsequence: consequence,
                rewrittenAlternative: alternative,
                constantValueOpt: null,
                rewrittenType: type);

            return new BoundSequence(
                syntax: syntax,
                locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment),
                value: conditionalExpression,
                type: type);
        }
        /// <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);
        }
        private BoundExpression RewriteIntPtrConversion(
            BoundConversion oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            MethodSymbol symbolOpt,
            bool @checked,
            bool explicitCastInCode,
            bool isExtensionMethod,
            bool isArrayIndex,
            ConstantValue constantValueOpt,
            TypeSymbol rewrittenType)
        {
            Debug.Assert(rewrittenOperand != null);
            Debug.Assert((object)rewrittenType != null);

            TypeSymbol source = rewrittenOperand.Type;
            TypeSymbol target = rewrittenType;

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

            if (t0 != target && s0 != source)
            {
                // UNDONE: RewriteLiftedIntPtrConversion

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

            SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType);
            MethodSymbol method = GetSpecialTypeMethod(syntax, member);
            Debug.Assert(!method.ReturnsVoid);
            Debug.Assert(method.ParameterCount == 1);

            rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked);

            var returnType = method.ReturnType;
            Debug.Assert((object)returnType != null);

            if (inExpressionLambda)
            {
                return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType);
            }

            var rewrittenCall =
                inExpressionLambda && oldNode != null
                ? new BoundConversion(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, returnType)
                : MakeCall(
                    syntax: syntax,
                    rewrittenReceiver: null,
                    method: method,
                    rewrittenArguments: ImmutableArray.Create<BoundExpression>(rewrittenOperand),
                    type: returnType);

            return MakeConversion(rewrittenCall, rewrittenType, @checked);
        }
        private BoundExpression RewriteUserDefinedConversion(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            MethodSymbol method,
            TypeSymbol rewrittenType,
            ConversionKind conversionKind)
        {
            Debug.Assert((object)method != null && !method.ReturnsVoid && method.ParameterCount == 1);
            if (rewrittenOperand.Type != method.ParameterTypes[0])
            {
                Debug.Assert(rewrittenOperand.Type.IsNullableType());
                Debug.Assert(rewrittenOperand.Type.GetNullableUnderlyingType() == method.ParameterTypes[0]);
                return RewriteLiftedUserDefinedConversion(syntax, rewrittenOperand, method, rewrittenType, conversionKind);
            }

            var conv = new Conversion(ConversionKind.ExplicitUserDefined, method, false);
            Debug.Assert(conv.IsValid);
            BoundExpression result =
                inExpressionLambda
                ? BoundConversion.Synthesized(syntax, rewrittenOperand, MakeConversion(syntax, conv, rewrittenOperand.Type, rewrittenType), false, true, default(ConstantValue), rewrittenType)
                : (BoundExpression)BoundCall.Synthesized(syntax, null, method, rewrittenOperand);
            Debug.Assert(result.Type == rewrittenType);
            return result;
        }
        private BoundExpression RewriteLiftedUserDefinedConversion(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            MethodSymbol method,
            TypeSymbol rewrittenType,
            ConversionKind conversionKind)
        {
            if (inExpressionLambda)
            {
                Conversion conv = MakeConversion(syntax, new Conversion(conversionKind, method, false), rewrittenOperand.Type, rewrittenType);
                return BoundConversion.Synthesized(syntax, rewrittenOperand, conv, false, true, default(ConstantValue), rewrittenType);
            }

            // DELIBERATE SPEC VIOLATION: 
            // The native compiler allows for a "lifted" conversion even when the return type of the conversion
            // not a non-nullable value type. For example, if we have a conversion from struct S to string,
            // then a "lifted" conversion from S? to string is considered by the native compiler to exist,
            // with the semantics of "s.HasValue ? (string)s.Value : (string)null".  The Roslyn compiler
            // perpetuates this error for the sake of backwards compatibility.

            Debug.Assert((object)rewrittenType != null);
            Debug.Assert(rewrittenOperand.Type.IsNullableType());

            BoundExpression optimized = OptimizeLiftedUserDefinedConversion(syntax, rewrittenOperand, conversionKind, method, rewrittenType);
            if (optimized != null)
            {
                return optimized;
            }

            // We have no optimizations we can perform. If the return type of the 
            // conversion method is a non-nullable value type R then we lower this as:
            //
            // temp = operand
            // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?)
            //
            // Otherwise, if the return type of the conversion is a nullable value type, reference type
            // or pointer type P, then we lower this as:
            //
            // temp = operand
            // temp.HasValue ? op_Whatever(temp.GetValueOrDefault()) : default(P)

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp = factory.StoreToTemp(rewrittenOperand, out tempAssignment);
            MethodSymbol get_HasValue = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_get_HasValue);
            MethodSymbol getValueOrDefault = GetNullableMethod(syntax, boundTemp.Type, SpecialMember.System_Nullable_T_GetValueOrDefault);

            // temp.HasValue
            BoundExpression condition = BoundCall.Synthesized(syntax, boundTemp, get_HasValue);

            // temp.GetValueOrDefault()
            BoundCall callGetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault);

            // op_Whatever(temp.GetValueOrDefault())
            BoundCall userDefinedCall = BoundCall.Synthesized(syntax, null, method, callGetValueOrDefault);

            // new R?(op_Whatever(temp.GetValueOrDefault())
            BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType);

            // default(R?)
            BoundExpression alternative = new BoundDefaultOperator(syntax, rewrittenType);

            // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?)
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: condition,
                rewrittenConsequence: consequence,
                rewrittenAlternative: alternative,
                constantValueOpt: null,
                rewrittenType: rewrittenType);

            // temp = operand
            // temp.HasValue ? new R?(op_Whatever(temp.GetValueOrDefault())) : default(R?)
            return new BoundSequence(
                syntax: syntax,
                locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment),
                value: conditionalExpression,
                type: rewrittenType);
        }
        private BoundExpression DistributeLiftedConversionIntoLiftedOperand(
            CSharpSyntaxNode syntax,
            BoundExpression operand,
            ConversionKind kind,
            bool @checked,
            MethodSymbol method,
            TypeSymbol type)
        {
            // Third, an even trickier optimization. Suppose we have a lifted conversion on top of
            // a lifted operation. Say, "decimal? d = M() + N()" where M() and N() return nullable ints.
            // We can codegen this naively as:
            //
            // int? m = M();
            // int? n = N();
            // int? r = m.HasValue && n.HasValue ? new int?(m.Value + n.Value) : new int?();
            // decimal? d = r.HasValue ? new decimal?((decimal)r.Value) : new decimal?();
            //
            // However, we also observe that we could do the conversion on both branches of the conditional:
            //
            // int? m = M();
            // int? n = N();
            // decimal? d = m.HasValue && n.HasValue ? (decimal?)(new int?(m.Value + n.Value)) : (decimal?)(new int?());
            //
            // And we already optimize those, above! So we could reduce this to:
            //
            // int? m = M();
            // int? n = N();
            // decimal? d = m.HasValue && n.HasValue ? new decimal?((decimal)(m.Value + n.Value)) : new decimal?());
            //
            // which avoids entirely the creation of the unnecessary nullable int!

            if (operand.Kind == BoundKind.Sequence)
            {
                BoundSequence seq = (BoundSequence)operand;
                if (seq.Value.Kind == BoundKind.ConditionalOperator)
                {
                    BoundConditionalOperator conditional = (BoundConditionalOperator)seq.Value;
                    Debug.Assert(seq.Type == conditional.Type);
                    Debug.Assert(conditional.Type == conditional.Consequence.Type);
                    Debug.Assert(conditional.Type == conditional.Alternative.Type);

                    if (NullableAlwaysHasValue(conditional.Consequence) != null && NullableNeverHasValue(conditional.Alternative))
                    {
                        return new BoundSequence(
                            seq.Syntax,
                            seq.Locals,
                            seq.SideEffects,
                            RewriteConditionalOperator(
                                conditional.Syntax,
                                conditional.Condition,
                                MakeConversion(null, syntax, conditional.Consequence, kind, method, @checked, false, false, false, ConstantValue.NotAvailable, type),
                                MakeConversion(null, syntax, conditional.Alternative, kind, method, @checked, false, false, false, ConstantValue.NotAvailable, type),
                                ConstantValue.NotAvailable,
                                type),
                            type);
                    }
                }
            }

            return null;
        }
        private BoundExpression OptimizeLiftedBuiltInConversion(
            CSharpSyntaxNode syntax,
            BoundExpression operand,
            ConversionKind kind,
            bool @checked,
            TypeSymbol type)
        {
            Debug.Assert(operand != null);
            Debug.Assert((object)type != null);

            // First, an optimization. If the source is known to always be null then
            // we can simply return the alternative.

            if (NullableNeverHasValue(operand))
            {
                return new BoundDefaultOperator(syntax, null, type);
            }

            // Second, a trickier optimization. If the conversion is "(T?)(new S?(x))" then
            // we generate "new T?((T)x)"

            BoundExpression nonNullValue = NullableAlwaysHasValue(operand);
            if (nonNullValue != null)
            {
                return new BoundObjectCreationExpression(
                    syntax,
                    GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor),
                    MakeConversion(
                        nonNullValue,
                        type.GetNullableUnderlyingType(),
                        @checked));
            }

            // Third, a very tricky optimization.
            return DistributeLiftedConversionIntoLiftedOperand(syntax, operand, kind, @checked, null, type);
        }
        private BoundExpression OptimizeLiftedUserDefinedConversion(
            CSharpSyntaxNode syntax,
            BoundExpression operand,
            ConversionKind kind,
            MethodSymbol method,
            TypeSymbol type)
        {
            // We begin with some optimizations: if the converted expression is known to always be null
            // then we can skip the whole thing and simply return the alternative:

            if (NullableNeverHasValue(operand))
            {
                return new BoundDefaultOperator(syntax, type);
            }

            // If the converted expression is known to never be null then we can return 
            // new R?(op_Whatever(nonNullableValue))
            BoundExpression nonNullValue = NullableAlwaysHasValue(operand);
            if (nonNullValue != null)
            {
                return MakeLiftedUserDefinedConversionConsequence(BoundCall.Synthesized(syntax, null, method, nonNullValue), type);
            }

            return DistributeLiftedConversionIntoLiftedOperand(syntax, operand, kind, false, method, type);
        }
Exemple #45
0
        private BoundExpression RewriteTupleConversion(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            bool @checked,
            bool explicitCastInCode,
            NamedTypeSymbol rewrittenType)
        {
            var destElementTypes = rewrittenType.GetElementTypesOfTupleOrCompatible();
            var numElements = destElementTypes.Length;

            ImmutableArray<FieldSymbol> srcElementFields;
            TypeSymbol srcType = rewrittenOperand.Type;

            if (srcType.IsTupleType)
            {
                srcElementFields = ((TupleTypeSymbol)srcType).TupleElementFields;
            }
            else
            {
                // The following codepath should be very uncommon (if reachable at all)
                // we should generally not see tuple compatible types in bound trees and 
                // see actual tuple types instead.
                Debug.Assert(srcType.IsTupleCompatible());

                // PERF: if allocations here become nuisance, consider caching the TupleTypeSymbol
                //       in the type symbols that can actually be tuple compatible
                srcElementFields = TupleTypeSymbol.Create((NamedTypeSymbol)srcType).TupleElementFields;
            }

            var fieldAccessorsBuilder = ArrayBuilder<BoundExpression>.GetInstance(numElements);

            BoundAssignmentOperator assignmentToTemp;
            var savedTuple = _factory.StoreToTemp(rewrittenOperand, out assignmentToTemp);

            for (int i = 0; i < numElements; i++)
            {
                var field = srcElementFields[i];

                DiagnosticInfo useSiteInfo = field.GetUseSiteDiagnostic();
                if ((object)useSiteInfo != null && useSiteInfo.Severity == DiagnosticSeverity.Error)
                {
                    Symbol.ReportUseSiteDiagnostic(useSiteInfo, _diagnostics, syntax.Location);
                }
                var fieldAccess = MakeTupleFieldAccess(syntax, field, savedTuple, null, LookupResultKind.Empty);
                var convertedFieldAccess = MakeConversion(fieldAccess, destElementTypes[i], @checked);
                fieldAccessorsBuilder.Add(convertedFieldAccess);
            }

            var result = MakeTupleCreationExpression(syntax, rewrittenType, fieldAccessorsBuilder.ToImmutableAndFree());
            return _factory.Sequence(savedTuple.LocalSymbol, assignmentToTemp, result);
        }
 public BoundExpression Convert(TypeSymbol type, BoundExpression arg, ConversionKind conversionKind, bool isChecked = false)
 {
     return new BoundConversion(Syntax, arg, conversionKind, null, isChecked, true, false, false, null, LookupResultKind.Viable, type) { WasCompilerGenerated = true };
 }
Exemple #47
0
        private BoundExpression RewriteIntPtrConversion(
            BoundConversion oldNode,
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenOperand,
            ConversionKind conversionKind,
            MethodSymbol symbolOpt,
            bool @checked,
            bool explicitCastInCode,
            bool isExtensionMethod,
            bool isArrayIndex,
            ConstantValue constantValueOpt,
            TypeSymbol rewrittenType)
        {
            Debug.Assert(rewrittenOperand != null);
            Debug.Assert((object)rewrittenType != null);

            TypeSymbol source = rewrittenOperand.Type;
            TypeSymbol target = rewrittenType;

            SpecialMember member = GetIntPtrConversionMethod(source: rewrittenOperand.Type, target: rewrittenType);
            MethodSymbol method = GetSpecialTypeMethod(syntax, member);
            Debug.Assert(!method.ReturnsVoid);
            Debug.Assert(method.ParameterCount == 1);

            if (source.IsNullableType() && target.IsNullableType())
            {
                Debug.Assert(target.IsNullableType());
                return RewriteLiftedUserDefinedConversion(syntax, rewrittenOperand, method, rewrittenType, ConversionKind.IntPtr);
            }
            else if (source.IsNullableType())
            {
                rewrittenOperand = MakeConversion(rewrittenOperand, source.StrippedType(), @checked);
            }

            rewrittenOperand = MakeConversion(rewrittenOperand, method.ParameterTypes[0], @checked);

            var returnType = method.ReturnType;
            Debug.Assert((object)returnType != null);

            if (_inExpressionLambda)
            {
                return BoundConversion.Synthesized(syntax, rewrittenOperand, new Conversion(conversionKind, method, false), @checked, explicitCastInCode, constantValueOpt, rewrittenType);
            }

            var rewrittenCall = MakeCall(
                    syntax: syntax,
                    rewrittenReceiver: null,
                    method: method,
                    rewrittenArguments: ImmutableArray.Create(rewrittenOperand),
                    type: returnType);

            return MakeConversion(rewrittenCall, rewrittenType, @checked);
        }
Exemple #48
0
        private static bool ReportAsOperatorConversionDiagnostics(
            CSharpSyntaxNode node,
            DiagnosticBag diagnostics,
            Compilation compilation,
            TypeSymbol operandType,
            TypeSymbol targetType,
            ConversionKind conversionKind,
            ConstantValue operandConstantValue)
        {
            // SPEC:    In an operation of the form E as T, E must be an expression and T must be a reference type,
            // SPEC:    a type parameter known to be a reference type, or a nullable type.
            // SPEC:    Furthermore, at least one of the following must be true, or otherwise a compile-time error occurs:
            // SPEC:    •	An identity (§6.1.1), implicit nullable (§6.1.4), implicit reference (§6.1.6), boxing (§6.1.7), 
            // SPEC:        explicit nullable (§6.2.3), explicit reference (§6.2.4), or unboxing (§6.2.5) conversion exists
            // SPEC:        from E to T.
            // SPEC:    •	The type of E or T is an open type.
            // SPEC:    •	E is the null literal.

            // SPEC VIOLATION:  The specification contains an error in the list of legal conversions above.
            // SPEC VIOLATION:  If we have "class C<T, U> where T : U where U : class" then there is
            // SPEC VIOLATION:  an implicit conversion from T to U, but it is not an identity, reference or
            // SPEC VIOLATION:  boxing conversion. It will be one of those at runtime, but at compile time
            // SPEC VIOLATION:  we do not know which, and therefore cannot classify it as any of those.
            // SPEC VIOLATION:  See Microsoft.CodeAnalysis.CSharp.UnitTests.SyntaxBinderTests.TestAsOperator_SpecErrorCase() test for an example.

            // SPEC VIOLATION:  The specification also unintentionally allows the case where requirement 2 above:
            // SPEC VIOLATION:  "The type of E or T is an open type" is true, but type of E is void type, i.e. T is an open type.
            // SPEC VIOLATION:  Dev10 compiler correctly generates an error for this case and we will maintain compatibility.

            bool hasErrors = false;
            switch (conversionKind)
            {
                case ConversionKind.ImplicitReference:
                case ConversionKind.Boxing:
                case ConversionKind.ImplicitNullable:
                case ConversionKind.Identity:
                case ConversionKind.ExplicitNullable:
                case ConversionKind.ExplicitReference:
                case ConversionKind.Unboxing:
                    break;

                default:
                    // Generate an error if there is no possible legal conversion and both the operandType
                    // and the targetType are closed types OR operandType is void type, otherwise we need a runtime check
                    if (!operandType.ContainsTypeParameter() && !targetType.ContainsTypeParameter() ||
                        operandType.SpecialType == SpecialType.System_Void)
                    {
                        SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, operandType, targetType);
                        Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, distinguisher.First, distinguisher.Second);
                        hasErrors = true;
                    }

                    break;
            }

            if (!hasErrors)
            {
                ReportAsOperatorConstantWarnings(node, diagnostics, operandType, targetType, conversionKind, operandConstantValue);
            }

            return hasErrors;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="binding"></param>
        /// <param name="expr"></param>
        /// <param name="ept1">expr -> TypeInParent</param>
        /// <param name="ept2">Type(expr) -> TypeInParent</param>
        private void ConversionTestHelper(SemanticModel semanticModel, ExpressionSyntax expr, ConversionKind ept1, ConversionKind ept2)
        {
            var info = semanticModel.GetTypeInfo(expr);
            Assert.NotNull(info);
            Assert.NotNull(info.ConvertedType);
            var conv = semanticModel.GetConversion(expr);

            // NOT expect NoConversion
            Conversion act1 = semanticModel.ClassifyConversion(expr, (TypeSymbol)info.ConvertedType);
            Assert.Equal(ept1, act1.Kind);
            ValidateConversion(act1, ept1);
            ValidateConversion(act1, conv.Kind);

            if (ept2 == ConversionKind.NoConversion)
            {
                Assert.Null(info.Type);
            }
            else
            {
                Assert.NotNull(info.Type);
                var act2 = semanticModel.Compilation.ClassifyConversion(info.Type, info.ConvertedType);
                Assert.Equal(ept2, act2.Kind);
                ValidateConversion(act2, ept2);
            }
        }
        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);
            }
        }
Exemple #51
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 ValidateConversion(Conversion conv, ConversionKind kind)
        {
            Assert.Equal(conv.Kind, kind);

            switch (kind)
            {
                case ConversionKind.NoConversion:
                    Assert.False(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    break;
                case ConversionKind.Identity:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsIdentity);
                    break;
                case ConversionKind.ImplicitNumeric:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsNumeric);
                    break;
                case ConversionKind.ImplicitEnumeration:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsEnumeration);
                    break;
                case ConversionKind.ImplicitNullable:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsNullable);
                    break;
                case ConversionKind.NullLiteral:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsNullLiteral);
                    break;
                case ConversionKind.ImplicitReference:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsReference);
                    break;
                case ConversionKind.Boxing:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsBoxing);
                    break;
                case ConversionKind.ImplicitDynamic:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsDynamic);
                    break;
                case ConversionKind.ExplicitDynamic:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsExplicit);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsDynamic);
                    break;
                case ConversionKind.ImplicitConstant:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsConstantExpression);
                    break;
                case ConversionKind.ImplicitUserDefined:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsUserDefined);
                    break;
                case ConversionKind.AnonymousFunction:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsAnonymousFunction);
                    break;
                case ConversionKind.MethodGroup:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.True(conv.IsMethodGroup);
                    break;
                case ConversionKind.ExplicitNumeric:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.True(conv.IsNumeric);
                    break;
                case ConversionKind.ExplicitEnumeration:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.True(conv.IsEnumeration);
                    break;
                case ConversionKind.ExplicitNullable:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.True(conv.IsNullable);
                    break;
                case ConversionKind.ExplicitReference:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.True(conv.IsReference);
                    break;
                case ConversionKind.Unboxing:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.True(conv.IsUnboxing);
                    break;
                case ConversionKind.ExplicitUserDefined:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.True(conv.IsUserDefined);
                    break;
                case ConversionKind.NullToPointer:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.False(conv.IsUserDefined);
                    Assert.True(conv.IsPointer);
                    break;
                case ConversionKind.PointerToVoid:
                    Assert.True(conv.Exists);
                    Assert.True(conv.IsImplicit);
                    Assert.False(conv.IsExplicit);
                    Assert.False(conv.IsUserDefined);
                    Assert.True(conv.IsPointer);
                    break;
                case ConversionKind.PointerToPointer:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.False(conv.IsUserDefined);
                    Assert.True(conv.IsPointer);
                    break;
                case ConversionKind.IntegerToPointer:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.False(conv.IsUserDefined);
                    Assert.True(conv.IsPointer);
                    break;
                case ConversionKind.PointerToInteger:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.False(conv.IsUserDefined);
                    Assert.True(conv.IsPointer);
                    break;
                case ConversionKind.IntPtr:
                    Assert.True(conv.Exists);
                    Assert.False(conv.IsImplicit);
                    Assert.True(conv.IsExplicit);
                    Assert.False(conv.IsUserDefined);
                    Assert.False(conv.IsPointer);
                    Assert.True(conv.IsIntPtr);
                    break;
            }
        }
Exemple #53
0
        private static void ReportAsOperatorConstantWarnings(
            CSharpSyntaxNode node,
            DiagnosticBag diagnostics,
            TypeSymbol operandType,
            TypeSymbol targetType,
            ConversionKind conversionKind,
            ConstantValue operandConstantValue)
        {
            // NOTE:    Even though BoundIsOperator and BoundAsOperator will always have no ConstantValue
            // NOTE:    (they are non-constant expressions according to Section 7.19 of the specification),
            // NOTE:    we want to perform constant analysis of is/as expressions to generate warnings if the
            // NOTE:    expression will always be true/false/null.

            ConstantValue constantValue = GetAsOperatorConstantResult(operandType, targetType, conversionKind, operandConstantValue);
            if (constantValue != null)
            {
                Debug.Assert(constantValue.IsNull);
                Error(diagnostics, ErrorCode.WRN_AlwaysNull, node, targetType);
            }
        }
Exemple #54
0
        /// <remarks>
        /// This method is intended for passes other than the LocalRewriter.
        /// Use MakeConversion helper method in the LocalRewriter instead,
        /// it generates a synthesized conversion in its lowered form.
        /// </remarks>
        public static BoundConversion SynthesizedNonUserDefined(CSharpSyntaxNode syntax, BoundExpression operand, ConversionKind kind, TypeSymbol type, ConstantValue constantValueOpt = null)
        {
            // We need more information than just the conversion kind for creating a synthesized user defined conversion.
            Debug.Assert(!kind.IsUserDefinedConversion(), "Use the BoundConversion.Synthesized overload that takes a 'Conversion' parameter for generating synthesized user defined conversions.");

            return new BoundConversion(
                syntax,
                operand,
                kind,
                resultKind: LookupResultKind.Viable, //not used
                isBaseConversion: false,
                symbolOpt: null,
                @checked: false,
                explicitCastInCode: false,
                isExtensionMethod: false,
                isArrayIndex: false,
                constantValueOpt: constantValueOpt,
                type: type)
            { WasCompilerGenerated = true };
        }
 /// <summary>
 /// Helper method to generate a lowered conversion from the given rewrittenOperand to the given rewrittenType with the given conversion kind.
 /// </summary>
 /// <remarks>
 /// Conversion kind must not be a user defined conversion, use the other overload which takes a 'Conversion' parameter for generating synthesized user defined conversions.
 /// </remarks>
 private BoundExpression MakeConversion(
     CSharpSyntaxNode syntax,
     BoundExpression rewrittenOperand,
     ConversionKind conversionKind,
     TypeSymbol rewrittenType,
     bool @checked,
     ConstantValue constantValueOpt = null)
 {
     // We need more information than just the conversion kind for creating a synthesized user defined conversion.
     Debug.Assert(!conversionKind.IsUserDefinedConversion(), "Use the other overload which takes a 'Conversion' parameter for generating synthesized user defined conversions.");
     return MakeConversion(syntax, rewrittenOperand, new Conversion(conversionKind), rewrittenType, @checked, explicitCastInCode: false, constantValueOpt: constantValueOpt);
 }
        private void ConversionTestHelper(SemanticModel semanticModel, ExpressionSyntax expr, ITypeSymbol expsym, ConversionKind expkind)
        {
            var info = semanticModel.GetTypeInfo(expr);
            Assert.NotNull(info);
            Assert.NotNull(info.ConvertedType);

            // NOT expect NoConversion
            Conversion act1 = semanticModel.ClassifyConversion(expr, expsym);
            Assert.Equal(expkind, act1.Kind);
            ValidateConversion(act1, expkind);
        }