public override BoundNode VisitConversion(BoundConversion node)
        {
            Debug.Assert(node != null);

            BoundExpression operand = (BoundExpression)this.Visit(node.Operand);

            switch (node.ConversionKind)
            {
                case ConversionKind.ImplicitNumeric:
                case ConversionKind.ExplicitNumeric:
                    if (node.Type.SpecialType == SpecialType.System_Decimal)
                    {
                        return RewriteNumericToDecimalConversion(node.Syntax, operand, operand.Type);
                    }
                    else if (operand.Type.SpecialType == SpecialType.System_Decimal)
                    {
                        return RewriteDecimalToNumericConversion(node.Syntax, operand, node.Type);
                    }
                    break;
                default:
                    break;
            }

            return node.Update(operand, node.ConversionKind, node.SymbolOpt, node.Checked, node.ExplicitCastInCode, node.IsExtensionMethod, node.ConstantValueOpt, node.ResultKind, node.Type);
        }
        private void EmitConversionExpression(BoundConversion conversion, bool used)
        {
            switch (conversion.ConversionKind)
            {
                case ConversionKind.MethodGroup:
                    EmitMethodGroupConversion(conversion, used);
                    return;
                case ConversionKind.NullToPointer:
                    // The null pointer is represented as 0u.
                    builder.EmitIntConstant(0);
                    builder.EmitOpCode(ILOpCode.Conv_u);
                    return;
            }

            if (!used && !ConversionHasSideEffects(conversion))
            {
                EmitExpression(conversion.Operand, false); // just do expr side effects
                return;
            }

            EmitExpression(conversion.Operand, true);
            EmitConversion(conversion);

            EmitPopIfUnused(used);
        }
        private static BoundExpression ConvertToLocalTypeHelper(CSharpCompilation compilation, BoundExpression expr, TypeSymbol type, DiagnosticBag diagnostics)
        {
            // NOTE: This conversion can fail if some of the types involved are from not-yet-loaded modules.
            // For example, if System.Exception hasn't been loaded, then this call will fail for $stowedexception.
            HashSet <DiagnosticInfo> useSiteDiagnostics = null;
            var conversion = compilation.Conversions.ClassifyConversionFromExpression(expr, type, ref useSiteDiagnostics);

            diagnostics.Add(expr.Syntax, useSiteDiagnostics);
            Debug.Assert(conversion.IsValid || diagnostics.HasAnyErrors());

            return(BoundConversion.Synthesized(
                       expr.Syntax,
                       expr,
                       conversion,
                       @checked: false,
                       explicitCastInCode: false,
                       constantValueOpt: null,
                       type: type,
                       hasErrors: !conversion.IsValid));
        }
        private void EmitSpecialUserDefinedConversion(BoundConversion conversion, bool used, BoundExpression operand)
        {
            var typeTo = (NamedTypeSymbol)conversion.Type;

            Debug.Assert((operand.Type.IsArray()) &&
                         this._module.Compilation.IsReadOnlySpanType(typeTo),
                         "only special kinds of conversions involving ReadOnlySpan may be handled in emit");

            if (!TryEmitReadonlySpanAsBlobWrapper(typeTo, operand, used, inPlace: false))
            {
                // there are several reasons that could prevent us from emitting a wrapper
                // in such case we just emit the operand and then invoke the conversion method
                EmitExpression(operand, used);
                if (used)
                {
                    // consumes 1 argument (array) and produces one result (span)
                    _builder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0);
                    EmitSymbolToken(conversion.SymbolOpt, conversion.Syntax, optArgList: null);
                }
            }
        }
Exemple #5
0
        private void EmitImplicitReferenceConversion(BoundConversion conversion)
        {
            // turn operand into an O(operandType)
            // if the operand is already verifiably an O, we can use it as-is
            // otherwise we need to box it, so that verifier will start tracking an O
            if (!conversion.Operand.Type.IsVerifierReference())
            {
                EmitBox(conversion.Operand.Type, conversion.Operand.Syntax);
            }

            // here we have O(operandType) that must be compatible with O(targetType)
            //
            // if target type is verifiably a reference type, we can leave the value as-is otherwise
            // we need to unbox to targetType to keep verifier happy.
            if (!conversion.Type.IsVerifierReference())
            {
                _builder.EmitOpCode(ILOpCode.Unbox_any);
                EmitSymbolToken(conversion.Type, conversion.Syntax);
            }

            return;
        }
Exemple #6
0
        private static bool ConversionHasSideEffects(BoundConversion conversion)
        {
            // only some intrinsic conversions are side effect free the only side effect of an
            // intrinsic conversion is a throw when we fail to convert.
            switch (conversion.ConversionKind)
            {
            case ConversionKind.Identity:
            // NOTE: even explicit float/double identity conversion does not have side
            // effects since it does not throw
            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ImplicitEnumeration:
            // implicit ref cast does not throw ...
            case ConversionKind.ImplicitReference:
            case ConversionKind.Boxing:
                return(false);

            // unchecked numeric conversion does not throw
            case ConversionKind.ExplicitNumeric:
                return(conversion.Checked);
            }

            return(true);
        }
Exemple #7
0
        public override BoundNode VisitIsOperator(BoundIsOperator node)
        {
            // rewrite is needed only for cases where there are no errors and
            // no warnings (i.e. non-constant result) generated during binding
            if (!node.HasErrors && node.ConstantValue == null)
            {
                BoundExpression operand     = node.Operand;
                var             targetType  = node.TargetType.Type;
                var             operandType = operand.Type;

                Debug.Assert(operandType != null);
                if (operandType.IsNullableType())
                {
                    //TODO: handle nullable types once nullable conversions are implemented
                }
                else if (!operandType.IsValueType)
                {
                    if (operandType.IsSameType(targetType))
                    {
                        // operand with bound identity or implicit conversion
                        // We can replace the "is" instruction with a null check
                        Visit(operand);
                        if (operandType.TypeKind == TypeKind.TypeParameter)
                        {
                            // We need to box the type parameter even if it is a known
                            // reference type to ensure there are no verifier errors
                            operand = new BoundConversion(operand.Syntax, operand.SyntaxTree, operand,
                                                          ConversionKind.Boxing, this.containingSymbol, false, false, null,
                                                          compilation.GetSpecialType(SpecialType.System_Object));
                        }
                        return(new BoundBinaryOperator(node.Syntax, node.SyntaxTree, BinaryOperatorKind.NotEqual,
                                                       operand, new BoundLiteral(null, null, ConstantValue.Null, null), null, node.Type));
                    }
                }
            }
            return(base.VisitIsOperator(node));
        }
        public override BoundNode VisitIsOperator(BoundIsOperator node)
        {
            // rewrite is needed only for cases where there are no errors and 
            // no warnings (i.e. non-constant result) generated during binding
            if (!node.HasErrors && node.ConstantValue == null)
            {
                BoundExpression operand = node.Operand;
                var targetType = node.TargetType.Type;
                var operandType = operand.Type;

                Debug.Assert(operandType != null);
                if (operandType.IsNullableType())
                {
                    //TODO: handle nullable types once nullable conversions are implemented
                }
                else if (!operandType.IsValueType)
                {
                    if (operandType.IsSameType(targetType))
                    {
                        // operand with bound identity or implicit conversion
                        // We can replace the "is" instruction with a null check
                        Visit(operand);
                        if(operandType.TypeKind == TypeKind.TypeParameter)
                        {
                            // We need to box the type parameter even if it is a known
                            // reference type to ensure there are no verifier errors
                            operand = new BoundConversion(operand.Syntax, operand.SyntaxTree, operand, 
                                ConversionKind.Boxing, this.containingSymbol, false, false, null, 
                                compilation.GetSpecialType(SpecialType.System_Object));
                        }
                        return new BoundBinaryOperator(node.Syntax, node.SyntaxTree, BinaryOperatorKind.NotEqual,
                            operand, new BoundLiteral(null, null, ConstantValue.Null, null), null, node.Type);
                    }
                }
            }
            return base.VisitIsOperator(node);
        }
        private void EmitConversion(BoundConversion conversion)
        {
            switch (conversion.ConversionKind)
            {
                case ConversionKind.Identity:
                    EmitIdentityConversion(conversion);
                    break;
                case ConversionKind.ImplicitNumeric:
                case ConversionKind.ExplicitNumeric:
                    EmitNumericConversion(conversion);
                    break;
                case ConversionKind.ImplicitReference:
                case ConversionKind.Boxing:
                    // from IL prospective ImplicitReference and Boxing conversions are the same thing.
                    // both force operand to be an object (O) - which may involve boxing 
                    // and then assume that result has the target type - which may involve unboxing.
                    EmitImplicitReferenceConversion(conversion);
                    break;
                case ConversionKind.ExplicitReference:
                case ConversionKind.Unboxing:
                    // from IL prospective ExplicitReference and UnBoxing conversions are the same thing.
                    // both force operand to be an object (O) - which may involve boxing 
                    // and then reinterpret result as the target type - which may involve unboxing.
                    EmitExplicitReferenceConversion(conversion);
                    break;
                case ConversionKind.ImplicitEnumeration:
                case ConversionKind.ExplicitEnumeration:
                    EmitEnumConversion(conversion);
                    break;
                case ConversionKind.ImplicitUserDefined:
                case ConversionKind.ExplicitUserDefined:
                case ConversionKind.AnonymousFunction:
                case ConversionKind.MethodGroup:
                case ConversionKind.ImplicitDynamic:
                case ConversionKind.ExplicitDynamic:
                    // None of these things should reach codegen (yet? maybe?)
                    throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);
                case ConversionKind.PointerToVoid:
                case ConversionKind.PointerToPointer:
                    return; //no-op since they all have the same runtime representation
                case ConversionKind.PointerToInteger:
                case ConversionKind.IntegerToPointer:
                    var fromType = conversion.Operand.Type;
                    var fromPredefTypeKind = fromType.PrimitiveTypeCode;

                    var toType = conversion.Type;
                    var toPredefTypeKind = toType.PrimitiveTypeCode;

#if DEBUG
                    switch (fromPredefTypeKind)
                    {
                        case Microsoft.Cci.PrimitiveTypeCode.IntPtr:
                        case Microsoft.Cci.PrimitiveTypeCode.UIntPtr:
                        case Microsoft.Cci.PrimitiveTypeCode.Pointer:
                            Debug.Assert(toPredefTypeKind.IsNumeric());
                            break;
                        default:
                            Debug.Assert(fromPredefTypeKind.IsNumeric());
                            Debug.Assert(
                                toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.IntPtr ||
                                toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.UIntPtr ||
                                toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.Pointer);
                            break;
                    }
#endif

                    builder.EmitNumericConversion(fromPredefTypeKind, toPredefTypeKind, conversion.Checked);
                    break;
                case ConversionKind.NullToPointer:
                    throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind); // Should be handled by caller.
                case ConversionKind.ImplicitNullable:
                case ConversionKind.ExplicitNullable:
                default:
                    throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);
            }
        }
 private void EmitMethodGroupConversion(BoundConversion conversion, bool used)
 {
     var group = (BoundMethodGroup)conversion.Operand;
     EmitDelegateCreation(conversion, group.InstanceOpt, conversion.IsExtensionMethod, conversion.SymbolOpt, conversion.Type, used);
 }
        private void EmitEnumConversion(BoundConversion conversion)
        {
            // Nullable enumeration conversions should have already been lowered into
            // implicit or explicit nullable conversions.
            Debug.Assert(!conversion.Type.IsNullableType());

            var fromType = conversion.Operand.Type;
            if (fromType.IsEnumType())
            {
                fromType = ((NamedTypeSymbol)fromType).EnumUnderlyingType;
            }

            var fromPredefTypeKind = fromType.PrimitiveTypeCode;
            Debug.Assert(fromPredefTypeKind.IsNumeric());

            var toType = conversion.Type;
            if (toType.IsEnumType())
            {
                toType = ((NamedTypeSymbol)toType).EnumUnderlyingType;
            }

            var toPredefTypeKind = toType.PrimitiveTypeCode;
            Debug.Assert(toPredefTypeKind.IsNumeric());

            builder.EmitNumericConversion(fromPredefTypeKind, toPredefTypeKind, conversion.Checked);
        }
        private void EmitExplicitReferenceConversion(BoundConversion conversion)
        {
            // turn operand into an O(operandType)
            // if the operand is already verifiably an O, we can use it as-is
            // otherwise we need to box it, so that verifier will start tracking an O
            if (!conversion.Operand.Type.IsVerifierReference())
            {
                EmitBox(conversion.Operand.Type, conversion.Operand.Syntax);
            }

            // here we have O(operandType) that could be compatible with O(targetType)
            // 
            // if target type is verifiably a reference type, we can just do a type check otherwise
            // we unbox which will both do the type check and start tracking actual target type in
            // verifier.
            if (conversion.Type.IsVerifierReference())
            {
                builder.EmitOpCode(ILOpCode.Castclass);
                EmitSymbolToken(conversion.Type, conversion.Syntax);
            }
            else
            {
                builder.EmitOpCode(ILOpCode.Unbox_any);
                EmitSymbolToken(conversion.Type, conversion.Syntax);
            }
        }
 private BoundConversion MakeImplicitReferenceConversionExplicit(BoundConversion implicitConv)
 {
     Debug.Assert(implicitConv.ConversionKind == ConversionKind.ImplicitReference);
     Debug.Assert(implicitConv.Type.TypeKind == TypeKind.Interface);
     return implicitConv.Update(
         operand: (BoundExpression)Visit(implicitConv.Operand), //NB: visit
         conversionKind: ConversionKind.ExplicitReference,
         symbolOpt: null,
         @checked: false,
         explicitCastInCode: false,
         constantValueOpt: implicitConv.ConstantValueOpt,
         type: implicitConv.Type);
 }
        private BoundNode RewriteMethodGroupConversion(BoundConversion conversion)
        {
            // in a method group conversion, we may need to rewrite the selected method
            BoundMethodGroup operand = (BoundMethodGroup)conversion.Operand;
            BoundExpression originalReceiverOpt = operand.ReceiverOpt;
            BoundExpression receiverOpt;

            if (originalReceiverOpt == null)
            {
                receiverOpt = null;
            }
            else if (!conversion.IsExtensionMethod && conversion.SymbolOpt.IsStatic)
            {
                receiverOpt = new BoundTypeExpression(originalReceiverOpt.Syntax, null, VisitType(originalReceiverOpt.Type));
            }
            else
            {
                receiverOpt = (BoundExpression)Visit(originalReceiverOpt);
            }

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

            MethodSymbol method = conversion.SymbolOpt;

            //  if the original receiver was a base access and is was rewritten, 
            //  change the method to point to the wrapper method
            if (BaseReferenceInReceiverWasRewritten(originalReceiverOpt, receiverOpt) && method.IsMetadataVirtual())
            {
                method = GetMethodWrapperForBaseNonVirtualCall(method, conversion.Syntax);
            }

            method = VisitMethodSymbol(method);
            operand = operand.Update(
                TypeMap.SubstituteTypesWithoutModifiers(operand.TypeArgumentsOpt),
                method.Name,
                operand.Methods,
                operand.LookupSymbolOpt,
                operand.LookupError,
                operand.Flags,
                receiverOpt,
                operand.ResultKind);

            var conversionInfo = conversion.Conversion;
            if (conversionInfo.Method != (object)method)
            {
                conversionInfo = conversionInfo.SetConversionMethod(method);
            }

            return conversion.Update(
                operand,
                conversionInfo,
                isBaseConversion: conversion.IsBaseConversion,
                @checked: conversion.Checked,
                explicitCastInCode: conversion.ExplicitCastInCode,
                constantValueOpt: conversion.ConstantValueOpt,
                type: type);
        }
Exemple #15
0
        private void EmitConversion(BoundConversion conversion)
        {
            switch (conversion.ConversionKind)
            {
            case ConversionKind.Identity:
                EmitIdentityConversion(conversion);
                break;

            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ExplicitNumeric:
                EmitNumericConversion(conversion);
                break;

            case ConversionKind.ImplicitReference:
            case ConversionKind.Boxing:
                // from IL prospective ImplicitReference and Boxing conversions are the same thing.
                // both force operand to be an object (O) - which may involve boxing
                // and then assume that result has the target type - which may involve unboxing.
                EmitImplicitReferenceConversion(conversion);
                break;

            case ConversionKind.ExplicitReference:
            case ConversionKind.Unboxing:
                // from IL prospective ExplicitReference and UnBoxing conversions are the same thing.
                // both force operand to be an object (O) - which may involve boxing
                // and then reinterpret result as the target type - which may involve unboxing.
                EmitExplicitReferenceConversion(conversion);
                break;

            case ConversionKind.ImplicitEnumeration:
            case ConversionKind.ExplicitEnumeration:
                EmitEnumConversion(conversion);
                break;

            case ConversionKind.ImplicitUserDefined:
            case ConversionKind.ExplicitUserDefined:
            case ConversionKind.AnonymousFunction:
            case ConversionKind.MethodGroup:
            case ConversionKind.ImplicitTupleLiteral:
            case ConversionKind.ImplicitTuple:
            case ConversionKind.ExplicitTuple:
            case ConversionKind.ImplicitDynamic:
            case ConversionKind.ExplicitDynamic:
                // None of these things should reach codegen (yet? maybe?)
                throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);

            case ConversionKind.PointerToVoid:
            case ConversionKind.PointerToPointer:
                return;     //no-op since they all have the same runtime representation

            case ConversionKind.PointerToInteger:
            case ConversionKind.IntegerToPointer:
                var fromType           = conversion.Operand.Type;
                var fromPredefTypeKind = fromType.PrimitiveTypeCode;

                var toType           = conversion.Type;
                var toPredefTypeKind = toType.PrimitiveTypeCode;

#if DEBUG
                switch (fromPredefTypeKind)
                {
                case Microsoft.Cci.PrimitiveTypeCode.IntPtr:
                case Microsoft.Cci.PrimitiveTypeCode.UIntPtr:
                case Microsoft.Cci.PrimitiveTypeCode.Pointer:
                    Debug.Assert(toPredefTypeKind.IsNumeric());
                    break;

                default:
                    Debug.Assert(fromPredefTypeKind.IsNumeric());
                    Debug.Assert(
                        toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.IntPtr ||
                        toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.UIntPtr ||
                        toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.Pointer);
                    break;
                }
#endif

                _builder.EmitNumericConversion(fromPredefTypeKind, toPredefTypeKind, conversion.Checked);
                break;

            case ConversionKind.NullToPointer:
                throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);     // Should be handled by caller.

            case ConversionKind.ImplicitNullable:
            case ConversionKind.ExplicitNullable:
            default:
                throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);
            }
        }
        /// <summary>
        /// The rewrites are as follows:
        /// 
        /// x++
        ///     temp = x
        ///     x = temp + 1
        ///     return temp
        /// x--
        ///     temp = x
        ///     x = temp - 1
        ///     return temp
        /// ++x
        ///     temp = x + 1
        ///     x = temp
        ///     return temp
        /// --x
        ///     temp = x - 1
        ///     x = temp
        ///     return temp
        ///     
        /// In each case, the literal 1 is of the type required by the builtin addition/subtraction operator that
        /// will be used.  The temp is of the same type as x, but the sum/difference may be wider, in which case a
        /// conversion is required.
        /// </summary>
        /// <param name="node">The unary operator expression representing the increment/decrement.</param>
        /// <param name="isPrefix">True for prefix, false for postfix.</param>
        /// <param name="isIncrement">True for increment, false for decrement.</param>
        /// <returns>A bound sequence that uses a temp to acheive the correct side effects and return value.</returns>
        private BoundNode LowerOperator(BoundUnaryOperator node, bool isPrefix, bool isIncrement)
        {
            BoundExpression operand = node.Operand;
            TypeSymbol operandType = operand.Type; //type of the variable being incremented
            Debug.Assert(operandType == node.Type);

            ConstantValue constantOne;
            BinaryOperatorKind binaryOperatorKind;
            MakeConstantAndOperatorKind(node.OperatorKind.OperandTypes(), node, out constantOne, out binaryOperatorKind);
            binaryOperatorKind |= isIncrement ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction;

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

            TypeSymbol constantType = compilation.GetSpecialType(constantOne.SpecialType);
            BoundExpression boundOne = new BoundLiteral(
                syntax: null,
                syntaxTree: null,
                constantValueOpt: constantOne,
                type: constantType);

            LocalSymbol tempSymbol = new TempLocalSymbol(operandType, RefKind.None, containingSymbol);
            BoundExpression boundTemp = new BoundLocal(
                syntax: null,
                syntaxTree: null,
                localSymbol: tempSymbol,
                constantValueOpt: null,
                type: operandType);

            // NOTE: the LHS may have a narrower type than the operator expects, but that
            // doesn't seem to cause any problems.  If a problem does arise, just add an
            // explicit BoundConversion.
            BoundExpression newValue = new BoundBinaryOperator(
                syntax: null,
                syntaxTree: null,
                operatorKind: binaryOperatorKind,
                left: isPrefix ? operand : boundTemp,
                right: boundOne,
                constantValueOpt: null,
                type: constantType);

            if (constantType != operandType)
            {
                newValue = new BoundConversion(
                    syntax: null,
                    syntaxTree: null,
                    operand: newValue,
                    conversionKind: operandType.IsEnumType() ? ConversionKind.ImplicitEnumeration : ConversionKind.ImplicitNumeric,
                    symbolOpt: null,
                    @checked: false,
                    explicitCastInCode: false,
                    constantValueOpt: null,
                    type: operandType);
            }

            ReadOnlyArray<BoundExpression> assignments = ReadOnlyArray<BoundExpression>.CreateFrom(
                new BoundAssignmentOperator(
                    syntax: null,
                    syntaxTree: null,
                    left: boundTemp,
                    right: isPrefix ? newValue : operand,
                    type: operandType),
                new BoundAssignmentOperator(
                    syntax: null,
                    syntaxTree: null,
                    left: operand,
                    right: isPrefix ? boundTemp : newValue,
                    type: operandType));

            return new BoundSequence(
                syntax: node.Syntax,
                syntaxTree: node.SyntaxTree,
                locals: ReadOnlyArray<LocalSymbol>.CreateFrom(tempSymbol),
                sideEffects: assignments,
                value: boundTemp,
                type: operandType);
        }
Exemple #17
0
            // private static T <Factory>(object[] submissionArray)
            // {
            //     var submission = new Submission#N(submissionArray);
            //     return submission.<Initialize>();
            // }
            internal override BoundBlock CreateBody()
            {
                var syntax = this.GetSyntax();

                var ctor = _containingType.GetScriptConstructor();

                Debug.Assert(ctor.ParameterCount == 1);

                var initializer = _containingType.GetScriptInitializer();

                Debug.Assert(initializer.ParameterCount == 0);

                var submissionArrayParameter = new BoundParameter(syntax, _parameters[0])
                {
                    WasCompilerGenerated = true
                };
                var submissionLocal = new BoundLocal(
                    syntax,
                    new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp),
                    null,
                    _containingType)
                {
                    WasCompilerGenerated = true
                };

                // var submission = new Submission#N(submissionArray);
                var submissionAssignment = new BoundExpressionStatement(
                    syntax,
                    new BoundAssignmentOperator(
                        syntax,
                        submissionLocal,
                        new BoundObjectCreationExpression(
                            syntax,
                            ctor,
                            ImmutableArray.Create <BoundExpression>(submissionArrayParameter),
                            default(ImmutableArray <string>),
                            default(ImmutableArray <RefKind>),
                            false,
                            default(ImmutableArray <int>),
                            null,
                            null,
                            _containingType)
                {
                    WasCompilerGenerated = true
                },
                        _containingType)
                {
                    WasCompilerGenerated = true
                })
                {
                    WasCompilerGenerated = true
                };

                // return submission.<Initialize>();
                BoundExpression initializeResult = new BoundCall(
                    syntax,
                    submissionLocal,
                    initializer,
                    ImmutableArray <BoundExpression> .Empty,
                    default(ImmutableArray <string>),
                    default(ImmutableArray <RefKind>),
                    isDelegateCall: false,
                    expanded: false,
                    invokedAsExtensionMethod: false,
                    argsToParamsOpt: default(ImmutableArray <int>),
                    resultKind: LookupResultKind.Viable,
                    type: initializer.ReturnType)
                {
                    WasCompilerGenerated = true
                };

                if (initializeResult.Type.IsStructType() && (_returnType.SpecialType == SpecialType.System_Object))
                {
                    initializeResult = new BoundConversion(syntax, initializeResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, _returnType)
                    {
                        WasCompilerGenerated = true
                    };
                }
                var returnStatement = new BoundReturnStatement(
                    syntax,
                    initializeResult)
                {
                    WasCompilerGenerated = true
                };

                return(new BoundBlock(syntax,
                                      ImmutableArray.Create <LocalSymbol>(submissionLocal.LocalSymbol),
                                      ImmutableArray.Create <BoundStatement>(submissionAssignment, returnStatement))
                {
                    WasCompilerGenerated = true
                });
            }
Exemple #18
0
        private static bool IsSafeForReordering(BoundExpression expression, RefKind kind)
        {
            // To be safe for reordering an expression must not cause any observable side effect *or
            // observe any side effect*. Accessing a local by value, for example, is possibly not
            // safe for reordering because reading a local can give a different result if reordered
            // with respect to a write elsewhere.

            var current = expression;

            while (true)
            {
                if (current.ConstantValue != null)
                {
                    return(true);
                }

                switch (current.Kind)
                {
                default:
                    return(false);

                case BoundKind.Parameter:
                case BoundKind.Local:
                    // A ref to a local variable or formal parameter is safe to reorder; it
                    // never has a side effect or consumes one.
                    return(kind != RefKind.None);

                case BoundKind.Conversion:
                {
                    BoundConversion conv = (BoundConversion)current;
                    switch (conv.ConversionKind)
                    {
                    case ConversionKind.AnonymousFunction:
                    case ConversionKind.ImplicitConstant:
                    case ConversionKind.MethodGroup:
                    case ConversionKind.NullLiteral:
                        return(true);

                    case ConversionKind.Boxing:
                    case ConversionKind.Dynamic:
                    case ConversionKind.ExplicitEnumeration:
                    case ConversionKind.ExplicitNullable:
                    case ConversionKind.ExplicitNumeric:
                    case ConversionKind.ExplicitReference:
                    case ConversionKind.Identity:
                    case ConversionKind.ImplicitEnumeration:
                    case ConversionKind.ImplicitNullable:
                    case ConversionKind.ImplicitNumeric:
                    case ConversionKind.ImplicitReference:
                    case ConversionKind.Unboxing:
                        current = conv.Operand;
                        break;

                    case ConversionKind.ExplicitUserDefined:
                    case ConversionKind.ImplicitUserDefined:
                        return(false);

                    default:
                        Debug.Fail("Unhandled conversion kind in reordering logic");
                        return(false);
                    }
                    break;
                }
                }
            }
        }
            // private static T <Factory>(object[] submissionArray) 
            // {
            //     var submission = new Submission#N(submissionArray);
            //     return submission.<Initialize>();
            // }
            internal override BoundBlock CreateBody()
            {
                var syntax = this.GetSyntax();

                var ctor = _containingType.GetScriptConstructor();
                Debug.Assert(ctor.ParameterCount == 1);

                var initializer = _containingType.GetScriptInitializer();
                Debug.Assert(initializer.ParameterCount == 0);

                var submissionArrayParameter = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true };
                var submissionLocal = new BoundLocal(
                    syntax,
                    new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp),
                    null,
                    _containingType)
                { WasCompilerGenerated = true };

                // var submission = new Submission#N(submissionArray);
                var submissionAssignment = new BoundExpressionStatement(
                    syntax,
                    new BoundAssignmentOperator(
                        syntax,
                        submissionLocal,
                        new BoundObjectCreationExpression(
                            syntax,
                            ctor,
                            ImmutableArray.Create<BoundExpression>(submissionArrayParameter),
                            default(ImmutableArray<string>),
                            default(ImmutableArray<RefKind>),
                            false,
                            default(ImmutableArray<int>),
                            null,
                            null,
                            _containingType)
                        { WasCompilerGenerated = true },
                        _containingType)
                    { WasCompilerGenerated = true })
                { WasCompilerGenerated = true };

                // return submission.<Initialize>();
                BoundExpression initializeResult = new BoundCall(
                    syntax,
                    submissionLocal,
                    initializer,
                    ImmutableArray<BoundExpression>.Empty,
                    default(ImmutableArray<string>),
                    default(ImmutableArray<RefKind>),
                    isDelegateCall: false,
                    expanded: false,
                    invokedAsExtensionMethod: false,
                    argsToParamsOpt: default(ImmutableArray<int>),
                    resultKind: LookupResultKind.Viable,
                    type: initializer.ReturnType)
                { WasCompilerGenerated = true };
                if (initializeResult.Type.IsStructType() && (_returnType.SpecialType == SpecialType.System_Object))
                {
                    initializeResult = new BoundConversion(syntax, initializeResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, _returnType)
                    { WasCompilerGenerated = true };
                }
                var returnStatement = new BoundReturnStatement(
                    syntax,
                    initializeResult)
                { WasCompilerGenerated = true };

                return new BoundBlock(syntax,
                    ImmutableArray.Create<LocalSymbol>(submissionLocal.LocalSymbol),
                    ImmutableArray.Create<BoundStatement>(submissionAssignment, returnStatement))
                { WasCompilerGenerated = true };
            }
        // Generates:
        // 
        // private static T {Factory}(InteractiveSession session) 
        // {
        //    T submissionResult;
        //    new {ThisScriptClass}(session, out submissionResult);
        //    return submissionResult;
        // }
        private BoundBlock CreateSubmissionFactoryBody()
        {
            Debug.Assert(_containingType.TypeKind == TypeKind.Submission);

            SyntaxTree syntaxTree = CSharpSyntaxTree.Dummy;
            CSharpSyntaxNode syntax = (CSharpSyntaxNode)syntaxTree.GetRoot();

            var interactiveSessionParam = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true };

            var ctor = _containingType.InstanceConstructors.Single();
            Debug.Assert(ctor is SynthesizedInstanceConstructor);
            Debug.Assert(ctor.ParameterCount == 2);

            var submissionResultType = ctor.Parameters[1].Type;

            var resultLocal = new SynthesizedLocal(ctor, submissionResultType, SynthesizedLocalKind.LoweringTemp);
            var localReference = new BoundLocal(syntax, resultLocal, null, submissionResultType) { WasCompilerGenerated = true };

            BoundExpression submissionResult = localReference;
            if (submissionResultType.IsStructType() && _returnType.SpecialType == SpecialType.System_Object)
            {
                submissionResult = new BoundConversion(syntax, submissionResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, _returnType)
                { WasCompilerGenerated = true };
            }

            return new BoundBlock(syntax,
                // T submissionResult;
                ImmutableArray.Create<LocalSymbol>(resultLocal),
                ImmutableArray.Create<BoundStatement>(
                    // new Submission(interactiveSession, out submissionResult);
                    new BoundExpressionStatement(syntax,
                        new BoundObjectCreationExpression(
                            syntax,
                            ctor,
                            ImmutableArray.Create<BoundExpression>(interactiveSessionParam, localReference),
                            ImmutableArray<string>.Empty,
                            ImmutableArray.Create<RefKind>(RefKind.None, RefKind.Ref),
                            false,
                            default(ImmutableArray<int>),
                            null,
                            null,
                            _containingType
                        )
                        { WasCompilerGenerated = true })
                    { WasCompilerGenerated = true },
                    // return submissionResult;
                    new BoundReturnStatement(syntax, submissionResult) { WasCompilerGenerated = true }))
            { WasCompilerGenerated = true };
        }
        // Generates:
        //
        // private static T {Factory}(InteractiveSession session)
        // {
        //    T submissionResult;
        //    new {ThisScriptClass}(session, out submissionResult);
        //    return submissionResult;
        // }
        private BoundBlock CreateSubmissionFactoryBody()
        {
            Debug.Assert(containingType.TypeKind == TypeKind.Submission);

            SyntaxTree       syntaxTree = CSharpSyntaxTree.Dummy;
            CSharpSyntaxNode syntax     = (CSharpSyntaxNode)syntaxTree.GetRoot();

            var interactiveSessionParam = new BoundParameter(syntax, parameters[0])
            {
                WasCompilerGenerated = true
            };

            var ctor = containingType.InstanceConstructors.Single();

            Debug.Assert(ctor is SynthesizedInstanceConstructor);
            Debug.Assert(ctor.ParameterCount == 2);

            var submissionResultType = ctor.Parameters[1].Type;

            var resultLocal    = new SynthesizedLocal(ctor, submissionResultType, SynthesizedLocalKind.LoweringTemp);
            var localReference = new BoundLocal(syntax, resultLocal, null, submissionResultType)
            {
                WasCompilerGenerated = true
            };

            BoundExpression submissionResult = localReference;

            if (submissionResultType.IsStructType() && this.returnType.SpecialType == SpecialType.System_Object)
            {
                submissionResult = new BoundConversion(syntax, submissionResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, this.returnType)
                {
                    WasCompilerGenerated = true
                };
            }

            return(new BoundBlock(syntax,
                                  // T submissionResult;
                                  ImmutableArray.Create <LocalSymbol>(resultLocal),
                                  ImmutableArray.Create <BoundStatement>(
                                      // new Submission(interactiveSession, out submissionResult);
                                      new BoundExpressionStatement(syntax,
                                                                   new BoundObjectCreationExpression(
                                                                       syntax,
                                                                       ctor,
                                                                       ImmutableArray.Create <BoundExpression>(interactiveSessionParam, localReference),
                                                                       ImmutableArray <string> .Empty,
                                                                       ImmutableArray.Create <RefKind>(RefKind.None, RefKind.Ref),
                                                                       false,
                                                                       default(ImmutableArray <int>),
                                                                       null,
                                                                       null,
                                                                       containingType
                                                                       )
            {
                WasCompilerGenerated = true
            })
            {
                WasCompilerGenerated = true
            },
                                      // return submissionResult;
                                      new BoundReturnStatement(syntax, submissionResult)
            {
                WasCompilerGenerated = true
            }))
            {
                WasCompilerGenerated = true
            });
        }
Exemple #22
0
 public override object VisitConversion(BoundConversion node, object arg)
 {
     Visit(node.Operand);
     return(null);
 }
        private void EmitIdentityConversion(BoundConversion conversion)
        {
            // An _explicit_ identity conversion from double to double or float to float on
            // non-constants must stay as a conversion. An _implicit_ identity conversion can be
            // optimized away.  Why? Because (double)d1 + d2 has different semantics than d1 + d2.
            // The former rounds off to 64 bit precision; the latter is permitted to use higher
            // precision math if d1 is enregistered.

            if (conversion.ExplicitCastInCode)
            {
                switch (conversion.Type.PrimitiveTypeCode)
                {
                    case Microsoft.Cci.PrimitiveTypeCode.Float32:
                    case Microsoft.Cci.PrimitiveTypeCode.Float64:
                        // For explicitly-written "identity conversions" from float to float or
                        // double to double, we require the generation of conv.r4 or conv.r8. The
                        // runtime can use these instructions to truncate precision, and csc.exe
                        // generates them. It's not ideal, we should consider the possibility of not
                        // doing this or marking somewhere else that this is necessary.

                        // Don't need to do this for constants, however.
                        if (conversion.Operand.ConstantValue == null)
                        {
                            EmitNumericConversion(conversion);
                        }
                        break;
                }
            }
        }
Exemple #24
0
        private void EmitMethodGroupConversion(BoundConversion conversion, bool used)
        {
            var group = (BoundMethodGroup)conversion.Operand;

            EmitDelegateCreation(conversion, group.InstanceOpt, conversion.IsExtensionMethod, conversion.SymbolOpt, conversion.Type, used);
        }
        private void EmitNumericConversion(BoundConversion conversion)
        {
            var fromType = conversion.Operand.Type;
            var fromPredefTypeKind = fromType.PrimitiveTypeCode;
            Debug.Assert(fromPredefTypeKind.IsNumeric());

            var toType = conversion.Type;
            var toPredefTypeKind = toType.PrimitiveTypeCode;
            Debug.Assert(toPredefTypeKind.IsNumeric());

            builder.EmitNumericConversion(fromPredefTypeKind, toPredefTypeKind, conversion.Checked);
        }
 public override BoundNode VisitConversion(BoundConversion conversion)
 {
     if (conversion.ConversionKind == ConversionKind.MethodGroup && (object)conversion.SymbolOpt != null)
     {
         return RewriteMethodGroupConversion(conversion);
     }
     else
     {
         return base.VisitConversion(conversion);
     }
 }
        private void EmitImplicitReferenceConversion(BoundConversion conversion)
        {
            // turn operand into an O(operandType)
            // if the operand is already verifiably an O, we can use it as-is
            // otherwise we need to box it, so that verifier will start tracking an O
            if (!conversion.Operand.Type.IsVerifierReference())
            {
                EmitBox(conversion.Operand.Type, conversion.Operand.Syntax);
            }

            // here we have O(operandType) that must be compatible with O(targetType)
            //
            // if target type is verifiably a reference type, we can leave the value as-is otherwise
            // we need to unbox to targetType to keep verifier happy.
            if (!conversion.Type.IsVerifierReference())
            {
                builder.EmitOpCode(ILOpCode.Unbox_any);
                EmitSymbolToken(conversion.Type, conversion.Syntax);
            }

            return;
        }
        private static bool ConversionHasSideEffects(BoundConversion conversion)
        {
            // only some intrinsic conversions are side effect free the only side effect of an
            // intrinsic conversion is a throw when we fail to convert.
            switch (conversion.ConversionKind)
            {
                case ConversionKind.Identity:
                // NOTE: even explicit float/double identity conversion does not have side
                // effects since it does not throw
                case ConversionKind.ImplicitNumeric:
                case ConversionKind.ImplicitEnumeration:
                // implicit ref cast does not throw ...
                case ConversionKind.ImplicitReference:
                case ConversionKind.Boxing:
                    return false;

                // unchecked numeric conversion does not throw 
                case ConversionKind.ExplicitNumeric:
                    return conversion.Checked;
            }

            return true;
        }
Exemple #29
0
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            if (node.HasErrors)
            {
                return(node);
            }

            // There are five possible cases.
            //
            // Case 1: receiver.Prop += value is transformed into
            // temp = receiver
            // temp.Prop = temp.Prop + value
            // and a later rewriting will turn that into calls to getters and setters.
            //
            // Case 2: collection[i1, i2, i3] += value is transformed into
            // tc = collection
            // t1 = i1
            // t2 = i2
            // t3 = i3
            // tc[t1, t2, t3] = tc[t1, t2, t3] + value
            // and again, a later rewriting will turn that into getters and setters of the indexer.
            //
            // Case 3: local += value (and param += value) needs no temporaries; it simply
            // becomes local = local + value.
            //
            // Case 4: staticField += value needs no temporaries either. However, classInst.field += value becomes
            // temp = classInst
            // temp.field = temp.field + value
            //
            // Case 5: otherwise, it must be structVariable.field += value or array[index] += value. Either way
            // we have a variable on the left. Transform it into:
            // ref temp = ref variable
            // temp = temp + value

            var temps = ArrayBuilder <LocalSymbol> .GetInstance();

            var stores = ArrayBuilder <BoundExpression> .GetInstance();

            // This will be filled in with the LHS that uses temporaries to prevent
            // double-evaluation of side effects.

            BoundExpression transformedLHS = null;

            if (node.Left.Kind == BoundKind.PropertyAccess)
            {
                // We need to stash away the receiver so that it does not get evaluated twice.
                // If the receiver is classified as a value of reference type then we can simply say
                //
                // R temp = receiver
                // temp.prop = temp.prop + rhs
                //
                // But if the receiver is classified as a variable of struct type then we
                // cannot make a copy of the value; we need to make sure that we mutate
                // the original receiver, not the copy.  We have to generate
                //
                // ref R temp = ref receiver
                // temp.prop = temp.prop + rhs
                //
                // The rules of C# (in section 7.17.1) require that if you have receiver.prop
                // as the target of an assignment such that receiver is a value type, it must
                // be classified as a variable. If we've gotten this far in the rewriting,
                // assume that was the case.

                var prop = (BoundPropertyAccess)node.Left;

                // If the property is static then we can just generate prop = prop + value
                if (prop.ReceiverOpt == null)
                {
                    transformedLHS = prop;
                }
                else
                {
                    // Can we ever avoid storing the receiver in a temp? If the receiver is a variable then it
                    // might be modified by the computation of the getter, the value, or the operation.
                    // The receiver cannot be a null constant or constant of value type. It could be a
                    // constant of string type, but there are no mutable properties of a string.
                    // Similarly, there are no mutable properties of a Type object, so the receiver
                    // cannot be a typeof(T) expression. The only situation I can think of where we could
                    // optimize away the temp is if the receiver is a readonly field of reference type,
                    // we are not in a constructor, and the receiver of the *field*, if any, is also idempotent.
                    // It doesn't seem worthwhile to pursue an optimization for this exceedingly rare case.

                    var rewrittenReceiver = (BoundExpression)Visit(prop.ReceiverOpt);
                    var receiverTemp      = TempHelpers.StoreToTemp(rewrittenReceiver, rewrittenReceiver.Type.IsValueType ? RefKind.Ref : RefKind.None, containingSymbol);
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                    transformedLHS = new BoundPropertyAccess(prop.Syntax, prop.SyntaxTree, receiverTemp.Item2, prop.PropertySymbol, prop.Type);
                }
            }
            else if (node.Left.Kind == BoundKind.IndexerAccess)
            {
                var             indexer             = (BoundIndexerAccess)node.Left;
                BoundExpression transformedReceiver = null;
                if (indexer.ReceiverOpt != null)
                {
                    var rewrittenReceiver = (BoundExpression)Visit(indexer.ReceiverOpt);
                    var receiverTemp      = TempHelpers.StoreToTemp(rewrittenReceiver, rewrittenReceiver.Type.IsValueType ? RefKind.Ref : RefKind.None, containingSymbol);
                    transformedReceiver = receiverTemp.Item2;
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                }

                // UNDONE: Dealing with the arguments is a bit tricky because they can be named out-of-order arguments;
                // UNDONE: we have to preserve both the source-code order of the side effects and the side effects
                // UNDONE: only being executed once.
                // UNDONE:
                // UNDONE: This is a subtly different problem than the problem faced by the conventional call
                // UNDONE: rewriter; with the conventional call rewriter we already know that the side effects
                // UNDONE: will only be executed once because the arguments are only being pushed on the stack once.
                // UNDONE: In a compound equality operator on an indexer the indices are placed on the stack twice.
                // UNDONE: That is to say, if you have:
                // UNDONE:
                // UNDONE: C().M(z : Z(), x : X(), y : Y())
                // UNDONE:
                // UNDONE: then we can rewrite that into
                // UNDONE:
                // UNDONE: tempc = C()
                // UNDONE: tempz = Z()
                // UNDONE: tempc.M(X(), Y(), tempz)
                // UNDONE:
                // UNDONE: See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the
                // UNDONE: temporaries in
                // UNDONE:
                // UNDONE: C().Collection[z : Z(), x : X(), y : Y()] += 1;
                // UNDONE:
                // UNDONE: because we have to ensure not just that Z() happens first, but in additioan that X() and Y() are only
                // UNDONE: called once.  We have to generate this as
                // UNDONE:
                // UNDONE: tempc = C().Collection
                // UNDONE: tempz = Z()
                // UNDONE: tempx = X()
                // UNDONE: tempy = Y()
                // UNDONE: tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1;
                // UNDONE:
                // UNDONE: Fortunately arguments to indexers are never ref or out, so we don't need to worry about that.
                // UNDONE: However, we can still do the optimization where constants are not stored in
                // UNDONE: temporaries; if we have
                // UNDONE:
                // UNDONE: C().Collection[z : 123, y : Y(), x : X()] += 1;
                // UNDONE:
                // UNDONE: Then we can generate that as
                // UNDONE:
                // UNDONE: tempc = C().Collection
                // UNDONE: tempx = X()
                // UNDONE: tempy = Y()
                // UNDONE: tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1;
                // UNDONE:
                // UNDONE: For now, we'll punt on both problems, as indexers are not implemented yet anyway.
                // UNDONE: We'll just generate one temporary for each argument. This will work, but in the
                // UNDONE: subsequent rewritings will generate more unnecessary temporaries.

                var transformedArguments = ArrayBuilder <BoundExpression> .GetInstance();

                foreach (var argument in indexer.Arguments)
                {
                    var rewrittenArgument = (BoundExpression)Visit(argument);
                    var argumentTemp      = TempHelpers.StoreToTemp(rewrittenArgument, RefKind.None, containingSymbol);
                    transformedArguments.Add(argumentTemp.Item2);
                    stores.Add(argumentTemp.Item1);
                    temps.Add(argumentTemp.Item2.LocalSymbol);
                }

                transformedLHS = new BoundIndexerAccess(indexer.Syntax, indexer.SyntaxTree, transformedArguments.ToReadOnlyAndFree(), transformedReceiver,
                                                        indexer.IndexerSymbol, indexer.Type);
            }
            else if (node.Left.Kind == BoundKind.Local || node.Left.Kind == BoundKind.Parameter)
            {
                // No temporaries are needed. Just generate local = local + value
                transformedLHS = node.Left;
            }
            else if (node.Left.Kind == BoundKind.FieldAccess)
            {
                // * If the field is static then no temporaries are needed.
                // * If the field is not static and the receiver is of reference type then generate t = r; t.f = t.f + value
                // * If the field is not static and the receiver is a variable of value type then we'll fall into the
                //   general variable case below.

                var fieldAccess = (BoundFieldAccess)node.Left;
                if (fieldAccess.ReceiverOpt == null)
                {
                    transformedLHS = fieldAccess;
                }
                else if (!fieldAccess.ReceiverOpt.Type.IsValueType)
                {
                    var rewrittenReceiver = (BoundExpression)Visit(fieldAccess.ReceiverOpt);
                    var receiverTemp      = TempHelpers.StoreToTemp(rewrittenReceiver, RefKind.None, containingSymbol);
                    stores.Add(receiverTemp.Item1);
                    temps.Add(receiverTemp.Item2.LocalSymbol);
                    transformedLHS = new BoundFieldAccess(fieldAccess.Syntax, fieldAccess.SyntaxTree, receiverTemp.Item2, fieldAccess.FieldSymbol, null);
                }
            }

            if (transformedLHS == null)
            {
                // We made no transformation above. Either we have array[index] += value or
                // structVariable.field += value; either way we have a potentially complicated variable-
                // producing expression on the left. Generate
                // ref temp = ref variable; temp = temp + value
                var rewrittenVariable = (BoundExpression)Visit(node.Left);
                var variableTemp      = TempHelpers.StoreToTemp(rewrittenVariable, RefKind.Ref, containingSymbol);
                stores.Add(variableTemp.Item1);
                temps.Add(variableTemp.Item2.LocalSymbol);
                transformedLHS = variableTemp.Item2;
            }

            // OK, we now have the temporary declarations, the temporary stores, and the transformed left hand side.
            // We need to generate
            //
            // xlhs = (FINAL)((LEFT)xlhs op rhs)
            //
            // And then wrap it up with the generated temporaries.
            //
            // (The right hand side has already been converted to the type expected by the operator.)

            BoundExpression opLHS = BoundConversion.SynthesizedConversion(transformedLHS, node.LeftConversion, node.Operator.LeftType);

            Debug.Assert(node.Right.Type == node.Operator.RightType);
            BoundExpression op         = new BoundBinaryOperator(null, null, node.Operator.Kind, opLHS, node.Right, null, node.Operator.ReturnType);
            BoundExpression opFinal    = BoundConversion.SynthesizedConversion(op, node.FinalConversion, node.Left.Type);
            BoundExpression assignment = new BoundAssignmentOperator(null, null, transformedLHS, opFinal, node.Left.Type);

            // OK, at this point we have:
            //
            // * temps evaluating and storing portions of the LHS that must be evaluated only once.
            // * the "transformed" left hand side, rebuilt to use temps where necessary
            // * the assignment "xlhs = (FINAL)((LEFT)xlhs op (RIGHT)rhs)"
            //
            // Notice that we have recursively rewritten the bound nodes that are things stored in
            // the temps, but we might have more rewriting to do on the assignment. There are three
            // conversions in there that might be lowered to method calls, an operator that might
            // be lowered to delegate combine, string concat, and so on, and don't forget, we
            // haven't lowered the right hand side at all! Let's rewrite all these things at once.

            BoundExpression rewrittenAssignment = (BoundExpression)Visit(assignment);

            BoundExpression result = (temps.Count == 0) ?
                                     rewrittenAssignment :
                                     new BoundSequence(null,
                                                       null,
                                                       temps.ToReadOnly(),
                                                       stores.ToReadOnly(),
                                                       rewrittenAssignment,
                                                       rewrittenAssignment.Type);

            temps.Free();
            stores.Free();
            return(result);
        }
Exemple #30
0
        private void EmitConversion(BoundConversion conversion)
        {
            switch (conversion.ConversionKind)
            {
            case ConversionKind.Identity:
                EmitIdentityConversion(conversion);
                break;

            case ConversionKind.ImplicitNumeric:
            case ConversionKind.ExplicitNumeric:
                EmitNumericConversion(conversion);
                break;

            case ConversionKind.ImplicitReference:
            case ConversionKind.Boxing:
                // from IL perspective ImplicitReference and Boxing conversions are the same thing.
                // both force operand to be an object (O) - which may involve boxing
                // and then assume that result has the target type - which may involve unboxing.
                EmitImplicitReferenceConversion(conversion);
                break;

            case ConversionKind.ExplicitReference:
            case ConversionKind.Unboxing:
                // from IL perspective ExplicitReference and UnBoxing conversions are the same thing.
                // both force operand to be an object (O) - which may involve boxing
                // and then reinterpret result as the target type - which may involve unboxing.
                EmitExplicitReferenceConversion(conversion);
                break;

            case ConversionKind.ImplicitEnumeration:
            case ConversionKind.ExplicitEnumeration:
                EmitEnumConversion(conversion);
                break;

            case ConversionKind.ImplicitUserDefined:
            case ConversionKind.ExplicitUserDefined:
            case ConversionKind.AnonymousFunction:
            case ConversionKind.MethodGroup:
            case ConversionKind.ImplicitTupleLiteral:
            case ConversionKind.ImplicitTuple:
            case ConversionKind.ExplicitTupleLiteral:
            case ConversionKind.ExplicitTuple:
            case ConversionKind.ImplicitDynamic:
            case ConversionKind.ExplicitDynamic:
            case ConversionKind.ImplicitThrow:
                // None of these things should reach codegen (yet? maybe?)
                throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);

            case ConversionKind.ImplicitPointerToVoid:
            case ConversionKind.ExplicitPointerToPointer:
            case ConversionKind.ImplicitPointer:
                return;     //no-op since they all have the same runtime representation

            case ConversionKind.ExplicitPointerToInteger:
            case ConversionKind.ExplicitIntegerToPointer:
                var fromType           = conversion.Operand.Type;
                var fromPredefTypeKind = fromType.PrimitiveTypeCode;

                var toType           = conversion.Type;
                var toPredefTypeKind = toType.PrimitiveTypeCode;

#if DEBUG
                switch (fromPredefTypeKind)
                {
                case Microsoft.Cci.PrimitiveTypeCode.IntPtr when !fromType.IsNativeIntegerType:
                case Microsoft.Cci.PrimitiveTypeCode.UIntPtr when !fromType.IsNativeIntegerType:
                case Microsoft.Cci.PrimitiveTypeCode.Pointer:
                case Microsoft.Cci.PrimitiveTypeCode.FunctionPointer:
                    Debug.Assert(IsNumeric(toType));
                    break;

                default:
                    Debug.Assert(IsNumeric(fromType));
                    Debug.Assert(
                        (toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.IntPtr || toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.UIntPtr) && !toType.IsNativeIntegerType ||
                        toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.Pointer ||
                        toPredefTypeKind == Microsoft.Cci.PrimitiveTypeCode.FunctionPointer);
                    break;
                }
#endif

                _builder.EmitNumericConversion(fromPredefTypeKind, toPredefTypeKind, conversion.Checked);
                break;

            case ConversionKind.PinnedObjectToPointer:
                // CLR allows unsafe conversion from(O) to native int/uint.
                // The conversion does not change the representation of the value,
                // but the value will not be reported to subsequent GC operations (and therefore will not be updated by such operations)
                _builder.EmitOpCode(ILOpCode.Conv_u);
                break;

            case ConversionKind.ImplicitNullToPointer:
                throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);     // Should be handled by caller.

            case ConversionKind.ImplicitNullable:
            case ConversionKind.ExplicitNullable:
            default:
                throw ExceptionUtilities.UnexpectedValue(conversion.ConversionKind);
            }
        }
Exemple #31
0
        /// <summary>
        /// The rewrites are as follows:
        ///
        /// x++
        ///     temp = x
        ///     x = temp + 1
        ///     return temp
        /// x--
        ///     temp = x
        ///     x = temp - 1
        ///     return temp
        /// ++x
        ///     temp = x + 1
        ///     x = temp
        ///     return temp
        /// --x
        ///     temp = x - 1
        ///     x = temp
        ///     return temp
        ///
        /// In each case, the literal 1 is of the type required by the builtin addition/subtraction operator that
        /// will be used.  The temp is of the same type as x, but the sum/difference may be wider, in which case a
        /// conversion is required.
        /// </summary>
        /// <param name="node">The unary operator expression representing the increment/decrement.</param>
        /// <param name="isPrefix">True for prefix, false for postfix.</param>
        /// <param name="isIncrement">True for increment, false for decrement.</param>
        /// <returns>A bound sequence that uses a temp to acheive the correct side effects and return value.</returns>
        private BoundNode LowerOperator(BoundUnaryOperator node, bool isPrefix, bool isIncrement)
        {
            BoundExpression operand     = node.Operand;
            TypeSymbol      operandType = operand.Type; //type of the variable being incremented

            Debug.Assert(operandType == node.Type);

            ConstantValue      constantOne;
            BinaryOperatorKind binaryOperatorKind;

            MakeConstantAndOperatorKind(node.OperatorKind.OperandTypes(), node, out constantOne, out binaryOperatorKind);
            binaryOperatorKind |= isIncrement ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction;

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

            TypeSymbol      constantType = compilation.GetSpecialType(constantOne.SpecialType);
            BoundExpression boundOne     = new BoundLiteral(
                syntax: null,
                syntaxTree: null,
                constantValueOpt: constantOne,
                type: constantType);

            LocalSymbol     tempSymbol = new TempLocalSymbol(operandType, RefKind.None, containingSymbol);
            BoundExpression boundTemp  = new BoundLocal(
                syntax: null,
                syntaxTree: null,
                localSymbol: tempSymbol,
                constantValueOpt: null,
                type: operandType);

            // NOTE: the LHS may have a narrower type than the operator expects, but that
            // doesn't seem to cause any problems.  If a problem does arise, just add an
            // explicit BoundConversion.
            BoundExpression newValue = new BoundBinaryOperator(
                syntax: null,
                syntaxTree: null,
                operatorKind: binaryOperatorKind,
                left: isPrefix ? operand : boundTemp,
                right: boundOne,
                constantValueOpt: null,
                type: constantType);

            if (constantType != operandType)
            {
                newValue = new BoundConversion(
                    syntax: null,
                    syntaxTree: null,
                    operand: newValue,
                    conversionKind: operandType.IsEnumType() ? ConversionKind.ImplicitEnumeration : ConversionKind.ImplicitNumeric,
                    symbolOpt: null,
                    @checked: false,
                    explicitCastInCode: false,
                    constantValueOpt: null,
                    type: operandType);
            }

            ReadOnlyArray <BoundExpression> assignments = ReadOnlyArray <BoundExpression> .CreateFrom(
                new BoundAssignmentOperator(
                    syntax : null,
                    syntaxTree : null,
                    left : boundTemp,
                    right : isPrefix ? newValue : operand,
                    type : operandType),
                new BoundAssignmentOperator(
                    syntax : null,
                    syntaxTree : null,
                    left : operand,
                    right : isPrefix ? boundTemp : newValue,
                    type : operandType));

            return(new BoundSequence(
                       syntax: node.Syntax,
                       syntaxTree: node.SyntaxTree,
                       locals: ReadOnlyArray <LocalSymbol> .CreateFrom(tempSymbol),
                       sideEffects: assignments,
                       value: boundTemp,
                       type: operandType));
        }