private BoundDynamicIndexerAccess TransformDynamicIndexerAccess(BoundDynamicIndexerAccess indexerAccess, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps)
        {
            BoundExpression loweredReceiver;

            if (CanChangeValueBetweenReads(indexerAccess.ReceiverOpt))
            {
                BoundAssignmentOperator assignmentToTemp;
                var temp = _factory.StoreToTemp(VisitExpression(indexerAccess.ReceiverOpt), out assignmentToTemp);
                stores.Add(assignmentToTemp);
                temps.Add(temp.LocalSymbol);
                loweredReceiver = temp;
            }
            else
            {
                loweredReceiver = indexerAccess.ReceiverOpt;
            }

            var arguments        = indexerAccess.Arguments;
            var loweredArguments = new BoundExpression[arguments.Length];

            for (int i = 0; i < arguments.Length; i++)
            {
                if (CanChangeValueBetweenReads(arguments[i]))
                {
                    BoundAssignmentOperator assignmentToTemp;
                    var temp = _factory.StoreToTemp(VisitExpression(arguments[i]), out assignmentToTemp, indexerAccess.ArgumentRefKindsOpt.RefKinds(i) != RefKind.None ? RefKind.Ref : RefKind.None);
                    stores.Add(assignmentToTemp);
                    temps.Add(temp.LocalSymbol);
                    loweredArguments[i] = temp;
                }
                else
                {
                    loweredArguments[i] = arguments[i];
                }
            }

            return(new BoundDynamicIndexerAccess(
                       indexerAccess.Syntax,
                       loweredReceiver,
                       loweredArguments.AsImmutableOrNull(),
                       indexerAccess.ArgumentNamesOpt,
                       indexerAccess.ArgumentRefKindsOpt,
                       indexerAccess.ApplicableIndexers,
                       indexerAccess.Type));
        }
        // Shared helper for MakeObjectCreationWithInitializer and MakeNewT
        private BoundExpression MakeObjectCreationWithInitializer(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenObjectCreation,
            BoundExpression initializerExpression,
            TypeSymbol type)
        {
            Debug.Assert(!inExpressionLambda);
            Debug.Assert(initializerExpression != null && !initializerExpression.HasErrors);

            // Create a temp and assign it with the object creation expression.
            BoundAssignmentOperator boundAssignmentToTemp;
            BoundLocal boundTemp = this.factory.StoreToTemp(rewrittenObjectCreation, out boundAssignmentToTemp);

            // Rewrite object/collection initializer expressions
            ArrayBuilder <BoundExpression> dynamicSiteInitializers = null;
            ArrayBuilder <BoundExpression> loweredInitializers     = ArrayBuilder <BoundExpression> .GetInstance();

            AddObjectOrCollectionInitializers(ref dynamicSiteInitializers, loweredInitializers, boundTemp, initializerExpression);

            int dynamicSiteCount = (dynamicSiteInitializers != null) ? dynamicSiteInitializers.Count : 0;

            var sideEffects = new BoundExpression[1 + dynamicSiteCount + loweredInitializers.Count];

            sideEffects[0] = boundAssignmentToTemp;

            if (dynamicSiteCount > 0)
            {
                dynamicSiteInitializers.CopyTo(sideEffects, 1);
                dynamicSiteInitializers.Free();
            }

            loweredInitializers.CopyTo(sideEffects, 1 + dynamicSiteCount);
            loweredInitializers.Free();

            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                       sideEffects: sideEffects.AsImmutableOrNull(),
                       value: boundTemp,
                       type: type));
        }
Example #3
0
        internal static ImmutableArray <BoundExpression> GetCallSiteArguments(BoundExpression callSiteFieldAccess, BoundExpression receiver, ImmutableArray <BoundExpression> arguments, BoundExpression right)
        {
            var result = new BoundExpression[1 + (receiver != null ? 1 : 0) + arguments.Length + (right != null ? 1 : 0)];
            int j      = 0;

            result[j++] = callSiteFieldAccess;

            if (receiver != null)
            {
                result[j++] = receiver;
            }

            arguments.CopyTo(result, j);
            j += arguments.Length;

            if (right != null)
            {
                result[j++] = right;
            }

            return(result.AsImmutableOrNull());
        }
        private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAccess, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps)
        {
            var receiverOpt = indexerAccess.ReceiverOpt;

            Debug.Assert(receiverOpt != null);

            BoundExpression transformedReceiver;

            if (CanChangeValueBetweenReads(receiverOpt))
            {
                BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                BoundAssignmentOperator assignmentToTemp;

                // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order),
                // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs.
                // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable
                // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp)
                // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed
                // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not,
                // SPEC VIOLATION: as value types.
                var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter;

                var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None);
                transformedReceiver = receiverTemp;
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);
            }
            else
            {
                transformedReceiver = VisitExpression(receiverOpt);
            }

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

            ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments);

            SyntaxNode               syntax           = indexerAccess.Syntax;
            PropertySymbol           indexer          = indexerAccess.Indexer;
            ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt;
            bool expanded = indexerAccess.Expanded;
            ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt;

            ImmutableArray <ParameterSymbol> parameters = indexer.Parameters;

            BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
            ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length);

            ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None);

            // Step one: Store everything that is non-trivial into a temporary; record the
            // stores in storesToTemps and make the actual argument a reference to the temp.
            // Do not yet attempt to deal with params arrays or optional arguments.
            BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKinds, rewrittenArguments, actualArguments, refKinds, storesToTemps);

            // Step two: If we have a params array, build the array and fill in the argument.
            if (expanded)
            {
                BoundExpression         array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
                BoundAssignmentOperator storeToTemp;
                var boundTemp = _factory.StoreToTemp(array, out storeToTemp);
                stores.Add(storeToTemp);
                temps.Add(boundTemp.LocalSymbol);
                actualArguments[actualArguments.Length - 1] = boundTemp;
            }

            // Step three: Now fill in the optional arguments. (Dev11 uses the getter for optional arguments in
            // compound assignments, but for deconstructions we use the setter if the getter is missing.)
            var accessor = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod();

            InsertMissingOptionalArguments(syntax, accessor.Parameters, actualArguments);

            // For a call, step four would be to optimize away some of the temps.  However, we need them all to prevent
            // duplicate side-effects, so we'll skip that step.

            if (indexer.ContainingType.IsComImport)
            {
                RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps);
            }

            rewrittenArguments = actualArguments.AsImmutableOrNull();

            foreach (BoundAssignmentOperator tempAssignment in storesToTemps)
            {
                temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol);
                stores.Add(tempAssignment);
            }

            storesToTemps.Free();
            argumentRefKinds = GetRefKindsOrNull(refKinds);
            refKinds.Free();

            // This is a temporary object that will be rewritten away before the lowering completes.
            return(new BoundIndexerAccess(
                       syntax,
                       transformedReceiver,
                       indexer,
                       rewrittenArguments,
                       default(ImmutableArray <string>),
                       argumentRefKinds,
                       false,
                       default(ImmutableArray <int>),
                       null,
                       indexerAccess.UseSetterForDefaultArgumentGeneration,
                       indexerAccess.Type));
        }
 public BoundExpression Array(TypeSymbol elementType, BoundExpression[] elements)
 {
     return Array(elementType, elements.AsImmutableOrNull());
 }
 public BoundExpression Sequence(BoundExpression[] sideEffects, BoundExpression result, TypeSymbol type = null)
 {
     return new BoundSequence(Syntax, ImmutableArray<LocalSymbol>.Empty, sideEffects.AsImmutableOrNull(), result, type ?? result.Type) { WasCompilerGenerated = true };
 }
        internal static ImmutableArray<BoundExpression> GetCallSiteArguments(BoundExpression callSiteFieldAccess, BoundExpression receiver, ImmutableArray<BoundExpression> arguments, BoundExpression right)
        {
            var result = new BoundExpression[1 + (receiver != null ? 1 : 0) + arguments.Length + (right != null ? 1 : 0)];
            int j = 0;

            result[j++] = callSiteFieldAccess;

            if (receiver != null)
            {
                result[j++] = receiver;
            }

            arguments.CopyTo(result, j);
            j += arguments.Length;

            if (right != null)
            {
                result[j++] = right;
            }

            return result.AsImmutableOrNull();
        }
        private BoundExpression MakeBinderConstruction(WellKnownMember factoryMethod, BoundExpression[] args)
        {
            var binderFactory = _factory.WellKnownMember(factoryMethod);
            if ((object)binderFactory == null)
            {
                return null;
            }

            return _factory.Call(null, (MethodSymbol)binderFactory, args.AsImmutableOrNull());
        }
Example #9
0
        /// <summary>
        /// Process tempStores and add them as sideeffects to arguments where needed. The return
        /// value tells how many temps are actually needed. For unnecessary temps the corresponding
        /// temp store will be cleared.
        /// </summary>
        private static int MergeArgumentsAndSideEffects(
            BoundExpression[] arguments,
            ArrayBuilder <RefKind> refKinds,
            ArrayBuilder <BoundAssignmentOperator> tempStores)
        {
            Debug.Assert(arguments != null);
            Debug.Assert(refKinds != null);
            Debug.Assert(tempStores != null);

            int tempsRemainedInUse = tempStores.Count;

            // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E()
            // and arguments: t0, t2, t1, t4, 10, t5
            //
            // We wish to produce arguments list: A(), SEQ(t1=B(), C()), t1, D(), 10, E()
            //
            // Our algorithm essentially finds temp stores that must happen before given argument
            // load, and if there are any they become side effects of the given load.
            // Stores immediately followed by loads of the same thing can be eliminated.
            //
            // Constraints:
            //    Stores must happen before corresponding loads.
            //    Stores cannot move relative to other stores. If arg was movable it would not need a temp.

            int firstUnclaimedStore = 0;

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

                // if argument is a load, search for corresponding store. if store is found, extract
                // the actual expression we were storing and add it as an argument - this one does
                // not need a temp. if there are any unclaimed stores before the found one, add them
                // as side effects that precede this arg, they cannot happen later.
                if (argument.Kind == BoundKind.Local)
                {
                    var correspondingStore = -1;
                    for (int i = firstUnclaimedStore; i < tempStores.Count; i++)
                    {
                        if (tempStores[i].Left == argument)
                        {
                            correspondingStore = i;
                            break;
                        }
                    }

                    // store found?
                    if (correspondingStore != -1)
                    {
                        var value = tempStores[correspondingStore].Right;

                        // When we created the temp, we dropped the argument RefKind
                        // since the local contained its own RefKind. Since we're removing
                        // the temp, the argument RefKind needs to be restored.
                        refKinds[a] = ((BoundLocal)argument).LocalSymbol.RefKind;

                        // the matched store will not need to go into sideffects, only ones before it will
                        // remove the store to signal that we are not using its temp.
                        tempStores[correspondingStore] = null;
                        tempsRemainedInUse--;

                        // no need for sideeffects?
                        // just combine store and load
                        if (correspondingStore == firstUnclaimedStore)
                        {
                            arguments[a] = value;
                        }
                        else
                        {
                            var sideffects = new BoundExpression[correspondingStore - firstUnclaimedStore];
                            for (int s = 0; s < sideffects.Length; s++)
                            {
                                sideffects[s] = tempStores[firstUnclaimedStore + s];
                            }

                            arguments[a] = new BoundSequence(
                                value.Syntax,
                                // this sequence does not own locals. Note that temps that
                                // we use for the rewrite are stored in one arg and loaded
                                // in another so they must live in a scope above.
                                ImmutableArray <LocalSymbol> .Empty,
                                sideffects.AsImmutableOrNull(),
                                value,
                                value.Type);
                        }

                        firstUnclaimedStore = correspondingStore + 1;
                    }
                }
            }

            Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all sideeffects were claimed");
            return(tempsRemainedInUse);
        }
Example #10
0
        private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpressionSyntax node, DiagnosticBag diagnostics)
        {
            //  prepare
            var initializers = node.Initializers;
            int fieldCount = initializers.Count;
            bool hasError = false;

            //  bind field initializers
            BoundExpression[] boundExpressions = new BoundExpression[fieldCount];
            AnonymousTypeField[] fields = new AnonymousTypeField[fieldCount];
            CSharpSyntaxNode[] fieldSyntaxNodes = new CSharpSyntaxNode[fieldCount];

            // WARNING: Note that SemanticModel.GetDeclaredSymbol for field initializer node relies on 
            //          the fact that the order of properties in anonymous type template corresponds 
            //          1-to-1 to the appropriate filed initializer syntax nodes; This means such 
            //          correspondence must be preserved all the time including erroneos scenarios

            // set of names already used
            HashSet<string> uniqueFieldNames = new HashSet<string>();

            for (int i = 0; i < fieldCount; i++)
            {
                AnonymousObjectMemberDeclaratorSyntax fieldInitializer = initializers[i];
                NameEqualsSyntax nameEquals = fieldInitializer.NameEquals;
                ExpressionSyntax expression = fieldInitializer.Expression;

                SyntaxToken nameToken = default(SyntaxToken);
                if (nameEquals != null)
                {
                    nameToken = nameEquals.Name.Identifier;
                }
                else
                {
                    nameToken = expression.ExtractAnonymousTypeMemberName();
                }

                hasError = hasError || expression.HasErrors;
                boundExpressions[i] = this.BindValue(expression, diagnostics, BindValueKind.RValue);

                //  check the name to be unique
                string fieldName = null;
                if (nameToken.Kind() == SyntaxKind.IdentifierToken)
                {
                    fieldName = nameToken.ValueText;
                    if (uniqueFieldNames.Contains(fieldName))
                    {
                        //  name duplication
                        Error(diagnostics, ErrorCode.ERR_AnonymousTypeDuplicatePropertyName, fieldInitializer);
                        hasError = true;
                        fieldName = null;
                    }
                    else
                    {
                        uniqueFieldNames.Add(fieldName);
                    }
                }
                else
                {
                    // there is something wrong with field's name
                    hasError = true;
                }

                //  calculate the expression's type and report errors if needed
                TypeSymbol fieldType = GetAnonymousTypeFieldType(boundExpressions[i], fieldInitializer, diagnostics, ref hasError);

                // build anonymous type field descriptor
                fieldSyntaxNodes[i] = (nameToken.Kind() == SyntaxKind.IdentifierToken) ? (CSharpSyntaxNode)nameToken.Parent : fieldInitializer;
                fields[i] = new AnonymousTypeField(fieldName == null ? "$" + i.ToString() : fieldName, fieldSyntaxNodes[i].Location, fieldType);

                //  NOTE: ERR_InvalidAnonymousTypeMemberDeclarator (CS0746) would be generated by parser if needed
            }

            //  Create anonymous type 
            AnonymousTypeManager manager = this.Compilation.AnonymousTypeManager;
            AnonymousTypeDescriptor descriptor = new AnonymousTypeDescriptor(fields.AsImmutableOrNull(), node.NewKeyword.GetLocation());
            NamedTypeSymbol anonymousType = manager.ConstructAnonymousTypeSymbol(descriptor);

            // declarators - bound nodes created for providing semantic info 
            // on anonymous type fields having explicitly specified name
            ArrayBuilder<BoundAnonymousPropertyDeclaration> declarators =
                ArrayBuilder<BoundAnonymousPropertyDeclaration>.GetInstance();
            for (int i = 0; i < fieldCount; i++)
            {
                NameEqualsSyntax explicitName = initializers[i].NameEquals;
                if (explicitName != null)
                {
                    AnonymousTypeField field = fields[i];
                    if (field.Name != null)
                    {
                        //  get property symbol and create a bound property declaration node
                        foreach (var symbol in anonymousType.GetMembers(field.Name))
                        {
                            if (symbol.Kind == SymbolKind.Property)
                            {
                                declarators.Add(new BoundAnonymousPropertyDeclaration(fieldSyntaxNodes[i], (PropertySymbol)symbol, field.Type));
                                break;
                            }
                        }
                    }
                }
            }

            // check if anonymous object creation is allowed in this context
            if (!this.IsAnonymousTypesAllowed())
            {
                Error(diagnostics, ErrorCode.ERR_AnonymousTypeNotAvailable, node.NewKeyword);
                hasError = true;
            }

            //  Finally create a bound node
            return new BoundAnonymousObjectCreationExpression(
                node,
                anonymousType.InstanceConstructors[0],
                boundExpressions.AsImmutableOrNull(),
                declarators.ToImmutableAndFree(),
                anonymousType,
                hasError);
        }
        public ImmutableArray<BoundExpression> TypeOfs(ImmutableArray<TypeSymbol> typeArguments)
        {
            var result = new BoundExpression[typeArguments.Length];
            for (int i = 0; i < typeArguments.Length; i++)
            {
                result[i] = Typeof(typeArguments[i]);
            }

            return result.AsImmutableOrNull();
        }
        private BoundDynamicIndexerAccess TransformDynamicIndexerAccess(BoundDynamicIndexerAccess indexerAccess, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
        {
            BoundExpression loweredReceiver;
            if (CanChangeValueBetweenReads(indexerAccess.ReceiverOpt))
            {
                BoundAssignmentOperator assignmentToTemp;
                var temp = _factory.StoreToTemp(VisitExpression(indexerAccess.ReceiverOpt), out assignmentToTemp);
                stores.Add(assignmentToTemp);
                temps.Add(temp.LocalSymbol);
                loweredReceiver = temp;
            }
            else
            {
                loweredReceiver = indexerAccess.ReceiverOpt;
            }

            var arguments = indexerAccess.Arguments;
            var loweredArguments = new BoundExpression[arguments.Length];

            for (int i = 0; i < arguments.Length; i++)
            {
                if (CanChangeValueBetweenReads(arguments[i]))
                {
                    BoundAssignmentOperator assignmentToTemp;
                    var temp = _factory.StoreToTemp(VisitExpression(arguments[i]), out assignmentToTemp, indexerAccess.ArgumentRefKindsOpt.RefKinds(i) != RefKind.None ? RefKind.Ref : RefKind.None);
                    stores.Add(assignmentToTemp);
                    temps.Add(temp.LocalSymbol);
                    loweredArguments[i] = temp;
                }
                else
                {
                    loweredArguments[i] = arguments[i];
                }
            }

            return new BoundDynamicIndexerAccess(
                indexerAccess.Syntax,
                loweredReceiver,
                loweredArguments.AsImmutableOrNull(),
                indexerAccess.ArgumentNamesOpt,
                indexerAccess.ArgumentRefKindsOpt,
                indexerAccess.ApplicableIndexers,
                indexerAccess.Type);
        }
        private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAccess, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps)
        {
            var receiverOpt = indexerAccess.ReceiverOpt;
            Debug.Assert(receiverOpt != null);

            BoundExpression transformedReceiver;
            if (CanChangeValueBetweenReads(receiverOpt))
            {
                BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                BoundAssignmentOperator assignmentToTemp;

                // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order),
                // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs.
                // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable 
                // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp)
                // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed
                // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not,
                // SPEC VIOLATION: as value types.
                var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter;

                var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None);
                transformedReceiver = receiverTemp;
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);
            }
            else
            {
                transformedReceiver = VisitExpression(receiverOpt);
            }

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

            ImmutableArray<BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments);

            SyntaxNode syntax = indexerAccess.Syntax;
            PropertySymbol indexer = indexerAccess.Indexer;
            ImmutableArray<RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt;
            bool expanded = indexerAccess.Expanded;
            ImmutableArray<int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt;

            ImmutableArray<ParameterSymbol> parameters = indexer.Parameters;
            BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
            ArrayBuilder<BoundAssignmentOperator> storesToTemps = ArrayBuilder<BoundAssignmentOperator>.GetInstance(rewrittenArguments.Length);
            ArrayBuilder<RefKind> refKinds = ArrayBuilder<RefKind>.GetInstance(parameters.Length, RefKind.None);

            // Step one: Store everything that is non-trivial into a temporary; record the
            // stores in storesToTemps and make the actual argument a reference to the temp.
            // Do not yet attempt to deal with params arrays or optional arguments.
            BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKinds, rewrittenArguments, actualArguments, refKinds, storesToTemps);

            // Step two: If we have a params array, build the array and fill in the argument.
            if (expanded)
            {
                BoundExpression array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
                BoundAssignmentOperator storeToTemp;
                var boundTemp = _factory.StoreToTemp(array, out storeToTemp);
                stores.Add(storeToTemp);
                temps.Add(boundTemp.LocalSymbol);
                actualArguments[actualArguments.Length - 1] = boundTemp;
            }

            // Step three: Now fill in the optional arguments. (Dev11 uses the
            // getter for optional arguments in compound assignments.)
            var getMethod = indexer.GetOwnOrInheritedGetMethod();
            Debug.Assert((object)getMethod != null);
            InsertMissingOptionalArguments(syntax, getMethod.Parameters, actualArguments);

            // For a call, step four would be to optimize away some of the temps.  However, we need them all to prevent
            // duplicate side-effects, so we'll skip that step.

            if (indexer.ContainingType.IsComImport)
            {
                RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps);
            }

            rewrittenArguments = actualArguments.AsImmutableOrNull();

            foreach (BoundAssignmentOperator tempAssignment in storesToTemps)
            {
                temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol);
                stores.Add(tempAssignment);
            }

            storesToTemps.Free();
            argumentRefKinds = GetRefKindsOrNull(refKinds);
            refKinds.Free();

            // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
            // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of indexers.
            return new BoundIndexerAccess(
                syntax,
                transformedReceiver,
                indexer,
                rewrittenArguments,
                default(ImmutableArray<string>),
                argumentRefKinds,
                false,
                default(ImmutableArray<int>),
                indexerAccess.Type);
        }
        /// <summary>
        /// In the expanded form of a compound assignment (or increment/decrement), the LHS appears multiple times.
        /// If we aren't careful, this can result in repeated side-effects.  This creates (ordered) temps for all of the
        /// subexpressions that could result in side-effects and returns a side-effect-free expression that can be used
        /// in place of the LHS in the expanded form.
        /// </summary>
        /// <param name="originalLHS">The LHS sub-expression of the compound assignment (or increment/decrement).</param>
        /// <param name="stores">Populated with a list of assignment expressions that initialize the temporary locals.</param>
        /// <param name="temps">Populated with a list of temporary local symbols.</param>
        /// <param name="isDynamicAssignment">True if the compound assignment is a dynamic operation.</param>
        /// <returns>
        /// A side-effect-free expression representing the LHS.
        /// The returned node needs to be lowered but its children are already lowered.
        /// </returns>
        private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalLHS, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps, bool isDynamicAssignment)
        {
            // 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

            switch (originalLHS.Kind)
            {
            case 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)originalLHS;

                // If the property is static or if the receiver is of kind "Base" or "this", then we can just generate prop = prop + value
                if (prop.ReceiverOpt == null || prop.PropertySymbol.IsStatic || !IntroducingReadCanBeObservable(prop.ReceiverOpt))
                {
                    return(prop);
                }

                Debug.Assert(prop.ReceiverOpt.Kind != BoundKind.TypeExpression);

                BoundExpression rewrittenReceiver = VisitExpression(prop.ReceiverOpt);

                BoundAssignmentOperator assignmentToTemp;

                // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order),
                // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs.
                // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable
                // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp)
                // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed
                // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not,
                // SPEC VIOLATION: as value types.
                var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter;

                var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None);
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);

                // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
                // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of properties.
                return(new BoundPropertyAccess(prop.Syntax, receiverTemp, prop.PropertySymbol, prop.ResultKind, prop.Type));
            }

            case BoundKind.DynamicMemberAccess:
            {
                var memberAccess = (BoundDynamicMemberAccess)originalLHS;
                if (!IntroducingReadCanBeObservable(memberAccess.Receiver))
                {
                    return(memberAccess);
                }

                // store receiver to temp:
                var rewrittenReceiver = VisitExpression(memberAccess.Receiver);
                BoundAssignmentOperator assignmentToTemp;
                var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp);
                stores.Add(assignmentToTemp);
                temps.Add(receiverTemp.LocalSymbol);

                return(new BoundDynamicMemberAccess(memberAccess.Syntax, receiverTemp, memberAccess.TypeArgumentsOpt, memberAccess.Name, memberAccess.Invoked, memberAccess.Indexed, memberAccess.Type));
            }

            case BoundKind.IndexerAccess:
            {
                BoundIndexerAccess indexerAccess = (BoundIndexerAccess)originalLHS;

                var receiverOpt = indexerAccess.ReceiverOpt;
                Debug.Assert(receiverOpt != null);

                BoundExpression transformedReceiver;
                if (IntroducingReadCanBeObservable(receiverOpt))
                {
                    BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                    BoundAssignmentOperator assignmentToTemp;

                    // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order),
                    // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs.
                    // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable
                    // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp)
                    // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed
                    // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not,
                    // SPEC VIOLATION: as value types.
                    var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter;

                    var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None);
                    transformedReceiver = receiverTemp;
                    stores.Add(assignmentToTemp);
                    temps.Add(receiverTemp.LocalSymbol);
                }
                else
                {
                    transformedReceiver = VisitExpression(receiverOpt);
                }

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

                ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments);

                CSharpSyntaxNode         syntax           = indexerAccess.Syntax;
                PropertySymbol           indexer          = indexerAccess.Indexer;
                ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt;
                bool expanded = indexerAccess.Expanded;
                ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt;

                ImmutableArray <ParameterSymbol> parameters          = indexer.Parameters;
                BoundExpression[] actualArguments                    = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
                ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length);

                ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None);

                // Step one: Store everything that is non-trivial into a temporary; record the
                // stores in storesToTemps and make the actual argument a reference to the temp.
                // Do not yet attempt to deal with params arrays or optional arguments.
                BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKinds, rewrittenArguments, actualArguments, refKinds, storesToTemps);

                // Step two: If we have a params array, build the array and fill in the argument.
                if (expanded)
                {
                    BoundExpression         array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
                    BoundAssignmentOperator storeToTemp;
                    var boundTemp = _factory.StoreToTemp(array, out storeToTemp);
                    stores.Add(storeToTemp);
                    temps.Add(boundTemp.LocalSymbol);
                    actualArguments[actualArguments.Length - 1] = boundTemp;
                }

                // Step three: Now fill in the optional arguments. (Dev11 uses the
                // getter for optional arguments in compound assignments.)
                var getMethod = indexer.GetOwnOrInheritedGetMethod();
                Debug.Assert((object)getMethod != null);
                InsertMissingOptionalArguments(syntax, getMethod.Parameters, actualArguments);

                // For a call, step four would be to optimize away some of the temps.  However, we need them all to prevent
                // duplicate side-effects, so we'll skip that step.

                if (indexer.ContainingType.IsComImport)
                {
                    RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps);
                }

                rewrittenArguments = actualArguments.AsImmutableOrNull();

                foreach (BoundAssignmentOperator tempAssignment in storesToTemps)
                {
                    temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol);
                    stores.Add(tempAssignment);
                }

                storesToTemps.Free();
                argumentRefKinds = GetRefKindsOrNull(refKinds);
                refKinds.Free();

                // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
                // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of indexers.
                return(new BoundIndexerAccess(
                           syntax,
                           transformedReceiver,
                           indexer,
                           rewrittenArguments,
                           default(ImmutableArray <string>),
                           argumentRefKinds,
                           false,
                           default(ImmutableArray <int>),
                           indexerAccess.Type));
            }

            case BoundKind.Local:
            case BoundKind.Parameter:
            case BoundKind.ThisReference:     // a special kind of parameter
                // No temporaries are needed. Just generate local = local + value
                return(originalLHS);

            case 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)originalLHS;
                BoundExpression receiverOpt = fieldAccess.ReceiverOpt;

                //If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value
                if (fieldAccess.FieldSymbol.IsStatic || !IntroducingReadCanBeObservable(receiverOpt))
                {
                    return(fieldAccess);
                }

                if (receiverOpt.Type.IsReferenceType)
                {
                    Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression);
                    BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                    if (rewrittenReceiver.Type.IsTypeParameter())
                    {
                        var memberContainingType = fieldAccess.FieldSymbol.ContainingType;

                        // From the verifier prospective type parameters do not contain fields or methods.
                        // the instance must be "boxed" to access the field
                        // It makes sense to box receiver before storing into a temp - no need to box twice.
                        rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType);
                    }

                    BoundAssignmentOperator assignmentToTemp;
                    var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp);
                    stores.Add(assignmentToTemp);
                    temps.Add(receiverTemp.LocalSymbol);
                    return(new BoundFieldAccess(fieldAccess.Syntax, receiverTemp, fieldAccess.FieldSymbol, null));
                }

                break;
            }

            case BoundKind.DynamicIndexerAccess:
            {
                var indexerAccess = (BoundDynamicIndexerAccess)originalLHS;

                BoundExpression loweredReceiver;
                if (IntroducingReadCanBeObservable(indexerAccess.ReceiverOpt))
                {
                    BoundAssignmentOperator assignmentToTemp;
                    var temp = _factory.StoreToTemp(VisitExpression(indexerAccess.ReceiverOpt), out assignmentToTemp);
                    stores.Add(assignmentToTemp);
                    temps.Add(temp.LocalSymbol);
                    loweredReceiver = temp;
                }
                else
                {
                    loweredReceiver = indexerAccess.ReceiverOpt;
                }

                var arguments        = indexerAccess.Arguments;
                var loweredArguments = new BoundExpression[arguments.Length];

                for (int i = 0; i < arguments.Length; i++)
                {
                    if (IntroducingReadCanBeObservable(arguments[i]))
                    {
                        BoundAssignmentOperator assignmentToTemp;
                        var temp = _factory.StoreToTemp(VisitExpression(arguments[i]), out assignmentToTemp, refKind: indexerAccess.ArgumentRefKindsOpt.RefKinds(i));
                        stores.Add(assignmentToTemp);
                        temps.Add(temp.LocalSymbol);
                        loweredArguments[i] = temp;
                    }
                    else
                    {
                        loweredArguments[i] = arguments[i];
                    }
                }

                return(new BoundDynamicIndexerAccess(
                           indexerAccess.Syntax,
                           loweredReceiver,
                           loweredArguments.AsImmutableOrNull(),
                           indexerAccess.ArgumentNamesOpt,
                           indexerAccess.ArgumentRefKindsOpt,
                           indexerAccess.ApplicableIndexers,
                           indexerAccess.Type));
            }

            case BoundKind.ArrayAccess:
                if (isDynamicAssignment)
                {
                    // In non-dynamic array[index] op= R we emit:
                    //   T& tmp = &array[index];
                    //   *tmp = *L op R;
                    // where T is the type of L.
                    //
                    // If L is an array access, the assignment is dynamic, the compile-time of the array is dynamic[]
                    // and the runtime type of the array is not object[] (but e.g. string[]) the pointer approach is broken.
                    // T is Object in such case and we can't take a read-write pointer of type Object& to an array element of non-object type.
                    //
                    // In this case we rewrite the assignment as follows:
                    //
                    //   E t_array = array;
                    //   I t_index = index; (possibly more indices)
                    //   T value = t_array[t_index];
                    //   t_array[t_index] = value op R;

                    var arrayAccess    = (BoundArrayAccess)originalLHS;
                    var loweredArray   = VisitExpression(arrayAccess.Expression);
                    var loweredIndices = VisitList(arrayAccess.Indices);

                    return(SpillArrayElementAccess(loweredArray, loweredIndices, stores, temps));
                }

                break;

            case BoundKind.PointerElementAccess:
            case BoundKind.PointerIndirectionOperator:
            case BoundKind.RefValueOperator:
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(originalLHS.Kind);
            }

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

            // Rewrite the variable.  Here we depend on the fact that the only forms
            // rewritten here are rewritten the same for lvalues and rvalues.
            BoundExpression rewrittenVariable = VisitExpression(originalLHS);

            BoundAssignmentOperator assignmentToTemp2;
            var variableTemp = _factory.StoreToTemp(rewrittenVariable, out assignmentToTemp2, refKind: RefKind.Ref);

            stores.Add(assignmentToTemp2);
            temps.Add(variableTemp.LocalSymbol);
            return(variableTemp);
        }
        // Shared helper for MakeObjectCreationWithInitializer and MakeNewT
        private BoundExpression MakeObjectCreationWithInitializer(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenObjectCreation,
            BoundExpression initializerExpression,
            TypeSymbol type)
        {
            Debug.Assert(!inExpressionLambda);
            Debug.Assert(initializerExpression != null && !initializerExpression.HasErrors);

            // Create a temp and assign it with the object creation expression.
            BoundAssignmentOperator boundAssignmentToTemp;
            BoundLocal boundTemp = this.factory.StoreToTemp(rewrittenObjectCreation, out boundAssignmentToTemp);

            // Rewrite object/collection initializer expressions
            ArrayBuilder<BoundExpression> dynamicSiteInitializers = null;
            ArrayBuilder<BoundExpression> loweredInitializers = ArrayBuilder<BoundExpression>.GetInstance();

            AddObjectOrCollectionInitializers(ref dynamicSiteInitializers, loweredInitializers, boundTemp, initializerExpression);

            int dynamicSiteCount = (dynamicSiteInitializers != null) ? dynamicSiteInitializers.Count : 0;

            var sideEffects = new BoundExpression[1 + dynamicSiteCount + loweredInitializers.Count];
            sideEffects[0] = boundAssignmentToTemp;

            if (dynamicSiteCount > 0)
            {
                dynamicSiteInitializers.CopyTo(sideEffects, 1);
                dynamicSiteInitializers.Free();
            }

            loweredInitializers.CopyTo(sideEffects, 1 + dynamicSiteCount);
            loweredInitializers.Free();

            return new BoundSequence(
                syntax: syntax,
                locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                sideEffects: sideEffects.AsImmutableOrNull(),
                value: boundTemp,
                type: type);
        }
Example #16
0
        /// <summary>
        /// Rewrites arguments of an invocation according to the receiving method or indexer.
        /// It is assumed that each argument has already been lowered, but we may need
        /// additional rewriting for the arguments, such as generating a params array, re-ordering
        /// arguments based on <paramref name="argsToParamsOpt"/> map, inserting arguments for optional parameters, etc.
        /// <paramref name="optionalParametersMethod"/> is the method used for values of any optional parameters.
        /// For indexers, this method must be an accessor, and for methods it must be the method
        /// itself. <paramref name="optionalParametersMethod"/> is needed for indexers since getter and setter
        /// may have distinct optional parameter values.
        /// </summary>
        private ImmutableArray<BoundExpression> MakeArguments(
            SyntaxNode syntax,
            ImmutableArray<BoundExpression> rewrittenArguments,
            Symbol methodOrIndexer,
            MethodSymbol optionalParametersMethod,
            bool expanded,
            ImmutableArray<int> argsToParamsOpt,
            ref ImmutableArray<RefKind> argumentRefKindsOpt,
            out ImmutableArray<LocalSymbol> temps,
            bool invokedAsExtensionMethod = false,
            ThreeState enableCallerInfo = ThreeState.Unknown)
        {
            // Either the methodOrIndexer is a property, in which case the method used
            // for optional parameters is an accessor of that property (or an overridden
            // property), or the methodOrIndexer is used for optional parameters directly.
            Debug.Assert(((methodOrIndexer.Kind == SymbolKind.Property) && optionalParametersMethod.IsAccessor()) ||
                ReferenceEquals(methodOrIndexer, optionalParametersMethod));

            // We need to do a fancy rewrite under the following circumstances:
            // (1) a params array is being used; we need to generate the array.
            // (2) there were named arguments that reordered the arguments; we might
            //     have to generate temporaries to ensure that the arguments are 
            //     evaluated in source code order, not the actual call order.
            // (3) there were optional parameters that had no corresponding arguments.
            //
            // If none of those are the case then we can just take an early out.

            // An applicable "vararg" method could not possibly be applicable in its expanded
            // form, and cannot possibly have named arguments or used optional parameters, 
            // because the __arglist() argument has to be positional and in the last position. 


            if (methodOrIndexer.GetIsVararg())
            {
                Debug.Assert(rewrittenArguments.Length == methodOrIndexer.GetParameterCount() + 1);
                Debug.Assert(argsToParamsOpt.IsDefault);
                Debug.Assert(!expanded);
                temps = default(ImmutableArray<LocalSymbol>);
                return rewrittenArguments;
            }

            var receiverNamedType = invokedAsExtensionMethod ?
                                    ((MethodSymbol)methodOrIndexer).Parameters[0].Type as NamedTypeSymbol :
                                    methodOrIndexer.ContainingType;

            bool isComReceiver = (object)receiverNamedType != null && receiverNamedType.IsComImport;

            if (rewrittenArguments.Length == methodOrIndexer.GetParameterCount() &&
                argsToParamsOpt.IsDefault &&
                !expanded &&
                !isComReceiver)
            {
                temps = default(ImmutableArray<LocalSymbol>);
                return rewrittenArguments;
            }


            // We have:
            // * a list of arguments, already converted to their proper types, 
            //   in source code order. Some optional arguments might be missing.
            // * a map showing which parameter each argument corresponds to. If
            //   this is null, then the argument to parameter mapping is one-to-one.
            // * the ref kind of each argument, in source code order. That is, whether
            //   the argument was marked as ref, out, or value (neither).
            // * a method symbol.
            // * whether the call is expanded or normal form.

            // We rewrite the call so that:
            // * if in its expanded form, we create the params array.
            // * if the call requires reordering of arguments because of named arguments, temporaries are generated as needed

            // Doing this transformation can move around refness in interesting ways. For example, consider
            //
            // A().M(y : ref B()[C()], x : out D());
            //
            // This will be created as a call with receiver A(), symbol M, argument list ( B()[C()], D() ),
            // name list ( y, x ) and ref list ( ref, out ).  We can rewrite this into temporaries:
            //
            // A().M( 
            //    seq ( ref int temp_y = ref B()[C()], out D() ),
            //    temp_y );
            // 
            // Now we have a call with receiver A(), symbol M, argument list as shown, no name list,
            // and ref list ( out, value ). We do not want to pass a *ref* to temp_y; the temporary
            // storage is not the thing being ref'd! We want to pass the *value* of temp_y, which
            // *contains* a reference.

            // We attempt to minimize the number of temporaries required. Arguments which neither
            // produce nor observe a side effect can be placed into their proper position without
            // recourse to a temporary. For example:
            //
            // Where(predicate: x=>x.Length!=0, sequence: S())
            //
            // can be rewritten without any temporaries because the conversion from lambda to
            // delegate does not produce any side effect that could be observed by S().
            //
            // By contrast:
            //
            // Foo(z: this.p, y: this.Q(), x: (object)10)
            //
            // The boxing of 10 can be reordered, but the fetch of this.p has to happen before the
            // call to this.Q() because the call could change the value of this.p. 
            //
            // We start by binding everything that is not obviously reorderable as a temporary, and
            // then run an optimizer to remove unnecessary temporaries.

            ImmutableArray<ParameterSymbol> parameters = methodOrIndexer.GetParameters();
            BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
            ArrayBuilder<BoundAssignmentOperator> storesToTemps = ArrayBuilder<BoundAssignmentOperator>.GetInstance(rewrittenArguments.Length);
            ArrayBuilder<RefKind> refKinds = ArrayBuilder<RefKind>.GetInstance(parameters.Length, RefKind.None);

            // Step one: Store everything that is non-trivial into a temporary; record the
            // stores in storesToTemps and make the actual argument a reference to the temp.
            // Do not yet attempt to deal with params arrays or optional arguments.
            BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKindsOpt, rewrittenArguments, actualArguments, refKinds, storesToTemps);


            // all the formal arguments, except missing optionals, are now in place. 
            // Optimize away unnecessary temporaries.
            // Necessary temporaries have their store instructions merged into the appropriate 
            // argument expression.
            ArrayBuilder<LocalSymbol> temporariesBuilder = ArrayBuilder<LocalSymbol>.GetInstance();
            OptimizeTemporaries(actualArguments, refKinds, storesToTemps, temporariesBuilder);

            // Step two: If we have a params array, build the array and fill in the argument.
            if (expanded)
            {
                actualArguments[actualArguments.Length - 1] = BuildParamsArray(syntax, methodOrIndexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
            }

            // Step three: Now fill in the optional arguments.
            InsertMissingOptionalArguments(syntax, optionalParametersMethod.Parameters, actualArguments, enableCallerInfo);

            if (isComReceiver)
            {
                RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temporariesBuilder);
            }

            temps = temporariesBuilder.ToImmutableAndFree();
            storesToTemps.Free();

            // * The refkind map is now filled out to match the arguments.
            // * The list of parameter names is now null because the arguments have been reordered.
            // * The args-to-params map is now null because every argument exactly matches its parameter.
            // * The call is no longer in its expanded form.

            argumentRefKindsOpt = GetRefKindsOrNull(refKinds);
            refKinds.Free();

            return actualArguments.AsImmutableOrNull();
        }
        /// <summary>
        /// In the expanded form of a compound assignment (or increment/decrement), the LHS appears multiple times.
        /// If we aren't careful, this can result in repeated side-effects.  This creates (ordered) temps for all of the
        /// subexpressions that could result in side-effects and returns a side-effect-free expression that can be used
        /// in place of the LHS in the expanded form.
        /// </summary>
        /// <param name="originalLHS">The LHS sub-expression of the compound assignment (or increment/decrement).</param>
        /// <param name="stores">Populated with a list of assignment expressions that initialize the temporary locals.</param>
        /// <param name="temps">Populated with a list of temporary local symbols.</param>
        /// <param name="isDynamicAssignment">True if the compound assignment is a dynamic operation.</param>
        /// <returns>
        /// A side-effect-free expression representing the LHS.
        /// The returned node needs to be lowered but its children are already lowered.
        /// </returns>
        private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalLHS, ArrayBuilder<BoundExpression> stores, ArrayBuilder<LocalSymbol> temps, bool isDynamicAssignment)
        {
            // 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

            switch (originalLHS.Kind)
            {
                case 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)originalLHS;

                        // If the property is static or is the receiver is of kind "Base" or "this", then we can just generate prop = prop + value
                        if (prop.ReceiverOpt == null || prop.PropertySymbol.IsStatic || !NeedsTemp(prop.ReceiverOpt))
                        {
                            return prop;
                        }

                        Debug.Assert(prop.ReceiverOpt.Kind != BoundKind.TypeExpression);

                        // 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 we know of is 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.

                        BoundExpression rewrittenReceiver = VisitExpression(prop.ReceiverOpt);
                        if (rewrittenReceiver.Type.IsTypeParameter() && rewrittenReceiver.Type.IsReferenceType)
                        {
                            var memberContainingType = prop.PropertySymbol.ContainingType;

                            // From the verifier prospective type parameters do not contain fields or methods.
                            // the instance must be boxed/constrained to access the member even if known to be a reference
                            // It makes sense to box reference receiver before storing into a temp - no need to box/constrain twice.
                            rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType);
                        }

                        BoundAssignmentOperator assignmentToTemp;
                        var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: rewrittenReceiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref);
                        stores.Add(assignmentToTemp);
                        temps.Add(receiverTemp.LocalSymbol);

                        // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
                        // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of properties.
                        return new BoundPropertyAccess(prop.Syntax, receiverTemp, prop.PropertySymbol, prop.ResultKind, prop.Type);
                    }

                case BoundKind.DynamicMemberAccess:
                    {
                        var memberAccess = (BoundDynamicMemberAccess)originalLHS;
                        if (!NeedsTemp(memberAccess.Receiver))
                        {
                            return memberAccess;
                        }

                        // store receiver to temp:
                        var rewrittenReceiver = VisitExpression(memberAccess.Receiver);
                        BoundAssignmentOperator assignmentToTemp;
                        var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp);
                        stores.Add(assignmentToTemp);
                        temps.Add(receiverTemp.LocalSymbol);

                        return new BoundDynamicMemberAccess(memberAccess.Syntax, receiverTemp, memberAccess.TypeArgumentsOpt, memberAccess.Name, memberAccess.Invoked, memberAccess.Indexed, memberAccess.Type);
                    }

                case BoundKind.IndexerAccess:
                    {
                        BoundIndexerAccess indexerAccess = (BoundIndexerAccess)originalLHS;

                        var receiverOpt = indexerAccess.ReceiverOpt;
                        Debug.Assert(receiverOpt != null);

                        BoundExpression transformedReceiver;
                        if (NeedsTemp(receiverOpt))
                        {
                            BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);
                            if (rewrittenReceiver.Type.IsTypeParameter() && rewrittenReceiver.Type.IsReferenceType)
                            {
                                var memberContainingType = indexerAccess.Indexer.ContainingType;

                                // From the verifier prospective type parameters do not contain fields or methods.
                                // the instance must be boxed/constrained to access the member even if known to be a reference
                                // It makes sense to box reference receiver before storing into a temp - no need to box/constrain twice.
                                rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType);
                            }

                            BoundAssignmentOperator assignmentToTemp;
                            var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: rewrittenReceiver.Type.IsReferenceType ? RefKind.None : RefKind.Ref);
                            transformedReceiver = receiverTemp;
                            stores.Add(assignmentToTemp);
                            temps.Add(receiverTemp.LocalSymbol);
                        }
                        else
                        {
                            transformedReceiver = VisitExpression(receiverOpt);
                        }

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

                        ImmutableArray<BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments);

                        CSharpSyntaxNode syntax = indexerAccess.Syntax;
                        PropertySymbol indexer = indexerAccess.Indexer;
                        ImmutableArray<RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt;
                        bool expanded = indexerAccess.Expanded;
                        ImmutableArray<int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt;

                        ImmutableArray<ParameterSymbol> parameters = indexer.Parameters;
                        BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
                        ArrayBuilder<BoundAssignmentOperator> storesToTemps = ArrayBuilder<BoundAssignmentOperator>.GetInstance(rewrittenArguments.Length);
                        ArrayBuilder<RefKind> refKinds = ArrayBuilder<RefKind>.GetInstance(parameters.Length, RefKind.None);

                        // Step one: Store everything that is non-trivial into a temporary; record the
                        // stores in storesToTemps and make the actual argument a reference to the temp.
                        // Do not yet attempt to deal with params arrays or optional arguments.
                        BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKinds, rewrittenArguments, actualArguments, refKinds, storesToTemps);

                        // Step two: If we have a params array, build the array and fill in the argument.
                        if (expanded)
                        {
                            BoundExpression array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
                            BoundAssignmentOperator storeToTemp;
                            var boundTemp = this.factory.StoreToTemp(array, out storeToTemp);
                            stores.Add(storeToTemp);
                            temps.Add(boundTemp.LocalSymbol);
                            actualArguments[actualArguments.Length - 1] = boundTemp;
                        }

                        // Step three: Now fill in the optional arguments. (Dev11 uses the
                        // getter for optional arguments in compound assignments.)
                        var getMethod = indexer.GetOwnOrInheritedGetMethod();
                        Debug.Assert((object)getMethod != null);
                        InsertMissingOptionalArguments(syntax, getMethod.Parameters, actualArguments);

                        // For a call, step four would be to optimize away some of the temps.  However, we need them all to prevent
                        // duplicate side-effects, so we'll skip that step.

                        if (indexer.ContainingType.IsComImport)
                        {
                            RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps);
                        }

                        rewrittenArguments = actualArguments.AsImmutableOrNull();

                        foreach (BoundAssignmentOperator tempAssignment in storesToTemps)
                        {
                            temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol);
                            stores.Add(tempAssignment);
                        }

                        storesToTemps.Free();
                        argumentRefKinds = GetRefKindsOrNull(refKinds);
                        refKinds.Free();

                        // CONSIDER: this is a temporary object that will be rewritten away before this lowering completes.
                        // Mitigation: this will only produce short-lived garbage for compound assignments and increments/decrements of indexers.
                        return new BoundIndexerAccess(
                            syntax,
                            transformedReceiver,
                            indexer,
                            rewrittenArguments,
                            default(ImmutableArray<string>),
                            argumentRefKinds,
                            false,
                            default(ImmutableArray<int>),
                            indexerAccess.Type);
                    }

                case BoundKind.Local:
                case BoundKind.Parameter:
                case BoundKind.ThisReference: // a special kind of parameter
                    // No temporaries are needed. Just generate local = local + value
                    return originalLHS;

                case BoundKind.DeclarationExpression:
                    var rewrittenDeclExpr = (BoundExpression)VisitDeclarationExpression((BoundDeclarationExpression)originalLHS);

                    switch (rewrittenDeclExpr.Kind)
                    {
                        case BoundKind.Local:
                            return rewrittenDeclExpr;

                        case BoundKind.Sequence:
                            var sequence = (BoundSequence)rewrittenDeclExpr;
                            stores.AddRange(sequence.SideEffects);
                            temps.AddRange(sequence.Locals);

                            if (sequence.Value.Kind == BoundKind.Local)
                            {
                                return sequence.Value;
                            }

                            break;
                    }

                    throw ExceptionUtilities.Unreachable;
                   
                case 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)originalLHS;
                        BoundExpression receiverOpt = fieldAccess.ReceiverOpt;

                        //If the receiver is static or is the receiver is of kind "Base" or "this", then we can just generate field = field + value
                        if (receiverOpt == null || fieldAccess.FieldSymbol.IsStatic || !NeedsTemp(receiverOpt))
                        {
                            return fieldAccess;
                        }

                        if (receiverOpt.Type.IsReferenceType)
                        {
                            Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression);
                            BoundExpression rewrittenReceiver = VisitExpression(receiverOpt);

                            if (rewrittenReceiver.Type.IsTypeParameter())
                            {
                                var memberContainingType = fieldAccess.FieldSymbol.ContainingType;

                                // From the verifier prospective type parameters do not contain fields or methods.
                                // the instance must be "boxed" to access the field
                                // It makes sense to box receiver before storing into a temp - no need to box twice.
                                rewrittenReceiver = BoxReceiver(rewrittenReceiver, memberContainingType);
                            }

                            BoundAssignmentOperator assignmentToTemp;
                            var receiverTemp = this.factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp);
                            stores.Add(assignmentToTemp);
                            temps.Add(receiverTemp.LocalSymbol);
                            return new BoundFieldAccess(fieldAccess.Syntax, receiverTemp, fieldAccess.FieldSymbol, null);
                        }

                        break;
                    }

                case BoundKind.DynamicIndexerAccess:
                    {
                        var indexerAccess = (BoundDynamicIndexerAccess)originalLHS;

                        BoundExpression loweredReceiver;
                        if (NeedsTemp(indexerAccess.ReceiverOpt))
                        {
                            BoundAssignmentOperator assignmentToTemp;
                            var temp = this.factory.StoreToTemp(VisitExpression(indexerAccess.ReceiverOpt), out assignmentToTemp);
                            stores.Add(assignmentToTemp);
                            temps.Add(temp.LocalSymbol);
                            loweredReceiver = temp;
                        }
                        else
                        {
                            loweredReceiver = indexerAccess.ReceiverOpt;
                        }

                        var arguments = indexerAccess.Arguments;
                        var loweredArguments = new BoundExpression[arguments.Length];

                        for (int i = 0; i < arguments.Length; i++)
                        {
                            if (NeedsTemp(arguments[i]))
                            {
                                BoundAssignmentOperator assignmentToTemp;
                                var temp = this.factory.StoreToTemp(VisitExpression(arguments[i]), out assignmentToTemp, refKind: indexerAccess.ArgumentRefKindsOpt.RefKinds(i));
                                stores.Add(assignmentToTemp);
                                temps.Add(temp.LocalSymbol);
                                loweredArguments[i] = temp;
                            }
                            else
                            {
                                loweredArguments[i] = arguments[i];
                            }
                        }

                        return new BoundDynamicIndexerAccess(
                            indexerAccess.Syntax,
                            loweredReceiver,
                            loweredArguments.AsImmutableOrNull(),
                            indexerAccess.ArgumentNamesOpt,
                            indexerAccess.ArgumentRefKindsOpt,
                            indexerAccess.ApplicableIndexers,
                            indexerAccess.Type);
                    }

                case BoundKind.ArrayAccess:
                    if (isDynamicAssignment)
                    {
                        // In non-dynamic array[index] op= R we emit:
                        //   T& tmp = &array[index];
                        //   *tmp = *L op R;
                        // where T is the type of L.
                        // 
                        // If L is an array access, the assignment is dynamic, the compile-time of the array is dynamic[] 
                        // and the runtime type of the array is not object[] (but e.g. string[]) the pointer approach is broken.
                        // T is Object in such case and we can't take a read-write pointer of type Object& to an array element of non-object type.
                        //
                        // In this case we rewrite the assignment as follows:
                        //
                        //   E t_array = array;
                        //   I t_index = index; (possibly more indices)
                        //   T value = t_array[t_index];
                        //   t_array[t_index] = value op R;

                        var arrayAccess = (BoundArrayAccess)originalLHS;
                        var loweredArray = VisitExpression(arrayAccess.Expression);
                        var loweredIndices = VisitList(arrayAccess.Indices);

                        return SpillArrayElementAccess(loweredArray, loweredIndices, stores, temps);
                    }

                    break;

                case BoundKind.PointerElementAccess:
                case BoundKind.PointerIndirectionOperator:
                case BoundKind.RefValueOperator:
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(originalLHS.Kind);
            }

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

            // Rewrite the variable.  Here we depend on the fact that the only forms
            // rewritten here are rewritten the same for lvalues and rvalues.
            BoundExpression rewrittenVariable = VisitExpression(originalLHS);

            BoundAssignmentOperator assignmentToTemp2;
            var variableTemp = this.factory.StoreToTemp(rewrittenVariable, out assignmentToTemp2, refKind: RefKind.Ref);
            stores.Add(assignmentToTemp2);
            temps.Add(variableTemp.LocalSymbol);
            return variableTemp;
        }
Example #18
0
        /// <summary>
        /// Process tempStores and add them as side-effects to arguments where needed. The return
        /// value tells how many temps are actually needed. For unnecessary temps the corresponding
        /// temp store will be cleared.
        /// </summary>
        private static int MergeArgumentsAndSideEffects(
            BoundExpression[] arguments,
            ArrayBuilder<RefKind> refKinds,
            ArrayBuilder<BoundAssignmentOperator> tempStores)
        {
            Debug.Assert(arguments != null);
            Debug.Assert(refKinds != null);
            Debug.Assert(tempStores != null);

            int tempsRemainedInUse = tempStores.Count;

            // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E()
            // and arguments: t0, t2, t1, t4, 10, t5
            //
            // We wish to produce arguments list: A(), SEQ(t1=B(), C()), t1, D(), 10, E()
            //
            // Our algorithm essentially finds temp stores that must happen before given argument
            // load, and if there are any they become side effects of the given load.
            // Stores immediately followed by loads of the same thing can be eliminated.
            //
            // Constraints:
            //    Stores must happen before corresponding loads.
            //    Stores cannot move relative to other stores. If arg was movable it would not need a temp.

            int firstUnclaimedStore = 0;

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

                // if argument is a load, search for corresponding store. if store is found, extract
                // the actual expression we were storing and add it as an argument - this one does
                // not need a temp. if there are any unclaimed stores before the found one, add them
                // as side effects that precede this arg, they cannot happen later.
                // NOTE: missing optional parameters are not filled yet and therefore nulls - no need to do anything for them
                if (argument?.Kind == BoundKind.Local)
                {
                    var correspondingStore = -1;
                    for (int i = firstUnclaimedStore; i < tempStores.Count; i++)
                    {
                        if (tempStores[i].Left == argument)
                        {
                            correspondingStore = i;
                            break;
                        }
                    }

                    // store found?
                    if (correspondingStore != -1)
                    {
                        var value = tempStores[correspondingStore].Right;

                        // When we created the temp, we dropped the argument RefKind
                        // since the local contained its own RefKind. Since we're removing
                        // the temp, the argument RefKind needs to be restored.
                        refKinds[a] = ((BoundLocal)argument).LocalSymbol.RefKind;

                        // the matched store will not need to go into side-effects, only ones before it will
                        // remove the store to signal that we are not using its temp.
                        tempStores[correspondingStore] = null;
                        tempsRemainedInUse--;

                        // no need for side-effects?
                        // just combine store and load
                        if (correspondingStore == firstUnclaimedStore)
                        {
                            arguments[a] = value;
                        }
                        else
                        {
                            var sideeffects = new BoundExpression[correspondingStore - firstUnclaimedStore];
                            for (int s = 0; s < sideeffects.Length; s++)
                            {
                                sideeffects[s] = tempStores[firstUnclaimedStore + s];
                            }

                            arguments[a] = new BoundSequence(
                                        value.Syntax,
                                        // this sequence does not own locals. Note that temps that
                                        // we use for the rewrite are stored in one arg and loaded
                                        // in another so they must live in a scope above.
                                        ImmutableArray<LocalSymbol>.Empty,
                                        sideeffects.AsImmutableOrNull(),
                                        value,
                                        value.Type);
                        }

                        firstUnclaimedStore = correspondingStore + 1;
                    }
                }
            }

            Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all side-effects were claimed");
            return tempsRemainedInUse;
        }
Example #19
0
        private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpressionSyntax node, DiagnosticBag diagnostics)
        {
            //  prepare
            var  initializers = node.Initializers;
            int  fieldCount   = initializers.Count;
            bool hasError     = false;

            //  bind field initializers
            BoundExpression[]    boundExpressions = new BoundExpression[fieldCount];
            AnonymousTypeField[] fields           = new AnonymousTypeField[fieldCount];
            CSharpSyntaxNode[]   fieldSyntaxNodes = new CSharpSyntaxNode[fieldCount];

            // WARNING: Note that SemanticModel.GetDeclaredSymbol for field initializer node relies on
            //          the fact that the order of properties in anonymous type template corresponds
            //          1-to-1 to the appropriate filed initializer syntax nodes; This means such
            //          correspondence must be preserved all the time including erroneos scenarios

            // set of names already used
            HashSet <string> uniqueFieldNames = new HashSet <string>();

            for (int i = 0; i < fieldCount; i++)
            {
                AnonymousObjectMemberDeclaratorSyntax fieldInitializer = initializers[i];
                NameEqualsSyntax nameEquals = fieldInitializer.NameEquals;
                ExpressionSyntax expression = fieldInitializer.Expression;

                SyntaxToken nameToken = default(SyntaxToken);
                if (nameEquals != null)
                {
                    nameToken = nameEquals.Name.Identifier;
                }
                else
                {
                    nameToken = expression.ExtractAnonymousTypeMemberName();
                }

                hasError            = hasError || expression.HasErrors;
                boundExpressions[i] = this.BindValue(expression, diagnostics, BindValueKind.RValue);

                //  check the name to be unique
                string fieldName = null;
                if (nameToken.CSharpKind() == SyntaxKind.IdentifierToken)
                {
                    fieldName = nameToken.ValueText;
                    if (uniqueFieldNames.Contains(fieldName))
                    {
                        //  name duplication
                        Error(diagnostics, ErrorCode.ERR_AnonymousTypeDuplicatePropertyName, fieldInitializer);
                        hasError  = true;
                        fieldName = null;
                    }
                    else
                    {
                        uniqueFieldNames.Add(fieldName);
                    }
                }
                else
                {
                    // there is something wrong with field's name
                    hasError = true;
                }

                //  calculate the expression's type and report errors if needed
                TypeSymbol fieldType = GetAnonymousTypeFieldType(boundExpressions[i], fieldInitializer, diagnostics, ref hasError);

                // build anonymous type field descriptor
                fieldSyntaxNodes[i] = (nameToken.CSharpKind() == SyntaxKind.IdentifierToken) ? (CSharpSyntaxNode)nameToken.Parent : fieldInitializer;
                fields[i]           = new AnonymousTypeField(fieldName == null ? '$' + i.ToString() : fieldName, fieldSyntaxNodes[i].Location, fieldType);

                //  NOTE: ERR_InvalidAnonymousTypeMemberDeclarator (CS0746) would be generated by parser if needed
            }

            //  Create anonymous type
            AnonymousTypeManager    manager       = this.Compilation.AnonymousTypeManager;
            AnonymousTypeDescriptor descriptor    = new AnonymousTypeDescriptor(fields.AsImmutableOrNull(), node.NewKeyword.GetLocation());
            NamedTypeSymbol         anonymousType = manager.ConstructAnonymousTypeSymbol(descriptor);

            // declarators - bound nodes created for providing semantic info
            // on anonymous type fields having explicitly specified name
            ArrayBuilder <BoundAnonymousPropertyDeclaration> declarators =
                ArrayBuilder <BoundAnonymousPropertyDeclaration> .GetInstance();

            for (int i = 0; i < fieldCount; i++)
            {
                NameEqualsSyntax explicitName = initializers[i].NameEquals;
                if (explicitName != null)
                {
                    AnonymousTypeField field = fields[i];
                    if (field.Name != null)
                    {
                        //  get property symbol and create a bound property declaration node
                        foreach (var symbol in anonymousType.GetMembers(field.Name))
                        {
                            if (symbol.Kind == SymbolKind.Property)
                            {
                                declarators.Add(new BoundAnonymousPropertyDeclaration(fieldSyntaxNodes[i], (PropertySymbol)symbol, field.Type));
                                break;
                            }
                        }
                    }
                }
            }

            // check if anonymous object creation is allowed in this context
            if (!this.IsAnonymousTypesAllowed())
            {
                Error(diagnostics, ErrorCode.ERR_AnonymousTypeNotAvailable, node.NewKeyword);
                hasError = true;
            }

            //  Finally create a bound node
            return(new BoundAnonymousObjectCreationExpression(
                       node,
                       anonymousType.InstanceConstructors[0],
                       boundExpressions.AsImmutableOrNull(),
                       declarators.ToImmutableAndFree(),
                       anonymousType,
                       hasError));
        }
Example #20
0
        /// <summary>
        /// Rewrites arguments of an invocation according to the receiving method or indexer.
        /// It is assumed that the each argument has already been lowered, but we may need
        /// additional rewriting for the arguments, such as generating a params array, re-ordering
        /// arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
        /// 'optionalParametersMethod' is the method used for values of any optional parameters.
        /// For indexers, this method must be an accessor, and for methods it must be the method
        /// itself. 'optionalParametersMethod' is needed for indexers since the getter and setter
        /// may have distinct optional parameter values.
        /// </summary>
        private ImmutableArray <BoundExpression> MakeArguments(
            CSharpSyntaxNode syntax,
            ImmutableArray <BoundExpression> rewrittenArguments,
            Symbol methodOrIndexer,
            MethodSymbol optionalParametersMethod,
            bool expanded,
            ImmutableArray <int> argsToParamsOpt,
            ref ImmutableArray <RefKind> argumentRefKindsOpt,
            out ImmutableArray <LocalSymbol> temps,
            bool invokedAsExtensionMethod = false)
        {
            // Either the methodOrIndexer is a property, in which case the method used
            // for optional parameters is an accessor of that property (or an overridden
            // property), or the methodOrIndexer is used for optional parameters directly.
            Debug.Assert(((methodOrIndexer.Kind == SymbolKind.Property) && optionalParametersMethod.IsAccessor()) ||
                         ReferenceEquals(methodOrIndexer, optionalParametersMethod));

            // We need to do a fancy rewrite under the following circumstances:
            // (1) a params array is being used; we need to generate the array.
            // (2) there were named arguments that reordered the arguments; we might
            //     have to generate temporaries to ensure that the arguments are
            //     evaluated in source code order, not the actual call order.
            // (3) there were optional parameters that had no corresponding arguments.
            //
            // If none of those are the case then we can just take an early out.

            // An applicable "vararg" method could not possibly be applicable in its expanded
            // form, and cannot possibly have named arguments or used optional parameters,
            // because the __arglist() argument has to be positional and in the last position.


            if (methodOrIndexer.GetIsVararg())
            {
                Debug.Assert(rewrittenArguments.Length == methodOrIndexer.GetParameterCount() + 1);
                Debug.Assert(argsToParamsOpt.IsDefault);
                Debug.Assert(!expanded);
                temps = default(ImmutableArray <LocalSymbol>);
                return(rewrittenArguments);
            }

            var receiverNamedType = invokedAsExtensionMethod ?
                                    ((MethodSymbol)methodOrIndexer).Parameters[0].Type as NamedTypeSymbol :
                                    methodOrIndexer.ContainingType;

            bool isComReceiver = (object)receiverNamedType != null && receiverNamedType.IsComImport;

            if (rewrittenArguments.Length == methodOrIndexer.GetParameterCount() &&
                argsToParamsOpt.IsDefault &&
                !expanded &&
                !isComReceiver)
            {
                temps = default(ImmutableArray <LocalSymbol>);
                return(rewrittenArguments);
            }


            // We have:
            // * a list of arguments, already converted to their proper types,
            //   in source code order. Some optional arguments might be missing.
            // * a map showing which parameter each argument corresponds to. If
            //   this is null, then the argument to parameter mapping is one-to-one.
            // * the ref kind of each argument, in source code order. That is, whether
            //   the argument was marked as ref, out, or value (neither).
            // * a method symbol.
            // * whether the call is expanded or normal form.

            // We rewrite the call so that:
            // * if in its expanded form, we create the params array.
            // * if the call requires reordering of arguments because of named arguments, temporaries are generated as needed

            // Doing this transformation can move around refness in interesting ways. For example, consider
            //
            // A().M(y : ref B()[C()], x : out D());
            //
            // This will be created as a call with receiver A(), symbol M, argument list ( B()[C()], D() ),
            // name list ( y, x ) and ref list ( ref, out ).  We can rewrite this into temporaries:
            //
            // A().M(
            //    seq ( ref int temp_y = ref B()[C()], out D() ),
            //    temp_y );
            //
            // Now we have a call with receiver A(), symbol M, argument list as shown, no name list,
            // and ref list ( out, value ). We do not want to pass a *ref* to temp_y; the temporary
            // storage is not the thing being ref'd! We want to pass the *value* of temp_y, which
            // *contains* a reference.

            // We attempt to minimize the number of temporaries required. Arguments which neither
            // produce nor observe a side effect can be placed into their proper position without
            // recourse to a temporary. For example:
            //
            // Where(predicate: x=>x.Length!=0, sequence: S())
            //
            // can be rewritten without any temporaries because the conversion from lambda to
            // delegate does not produce any side effect that could be observed by S().
            //
            // By contrast:
            //
            // Foo(z: this.p, y: this.Q(), x: (object)10)
            //
            // The boxing of 10 can be reordered, but the fetch of this.p has to happen before the
            // call to this.Q() because the call could change the value of this.p.
            //
            // We start by binding everything that is not obviously reorderable as a temporary, and
            // then run an optimizer to remove unnecessary temporaries.

            ImmutableArray <ParameterSymbol> parameters = methodOrIndexer.GetParameters();

            BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter.
            ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length);

            ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None);

            // Step one: Store everything that is non-trivial into a temporary; record the
            // stores in storesToTemps and make the actual argument a reference to the temp.
            // Do not yet attempt to deal with params arrays or optional arguments.
            BuildStoresToTemps(expanded, argsToParamsOpt, argumentRefKindsOpt, rewrittenArguments, actualArguments, refKinds, storesToTemps);

            // Step two: If we have a params array, build the array and fill in the argument.
            if (expanded)
            {
                actualArguments[actualArguments.Length - 1] = BuildParamsArray(syntax, methodOrIndexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]);
            }

            // Step three: Now fill in the optional arguments.
            InsertMissingOptionalArguments(syntax, optionalParametersMethod.Parameters, actualArguments);

            // Step four: all the arguments are now in place. Optimize away unnecessary temporaries.
            // Necessary temporaries have their store instructions merged into the appropriate
            // argument expression.
            ArrayBuilder <LocalSymbol> temporariesBuilder = ArrayBuilder <LocalSymbol> .GetInstance();

            OptimizeTemporaries(actualArguments, refKinds, storesToTemps, temporariesBuilder);

            if (isComReceiver)
            {
                RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temporariesBuilder);
            }

            temps = temporariesBuilder.ToImmutableAndFree();
            storesToTemps.Free();

            // * The refkind map is now filled out to match the arguments.
            // * The list of parameter names is now null because the arguments have been reordered.
            // * The args-to-params map is now null because every argument exactly matches its parameter.
            // * The call is no longer in its expanded form.

            argumentRefKindsOpt = GetRefKindsOrNull(refKinds);
            refKinds.Free();

            return(actualArguments.AsImmutableOrNull());
        }