public override BoundNode VisitFieldAccess(BoundFieldAccess node)
 {
     Debug.Assert(node != null);
     var constantValue = node.ConstantValue;
     if (!node.HasErrors && constantValue != null && constantValue.IsBad)
     {
         //we depend on whoever set the value to Bad to have added an appropriate diagnostic
         return new BoundFieldAccess(node.Syntax, node.SyntaxTree, node.ReceiverOpt, node.FieldSymbol, constantValue, true);
     }
     return base.VisitFieldAccess(node);
 }
 internal BoundExpression ToBoundExpression(CSharpSyntaxNode syntax)
 {
     var expr = this.DisplayClassInstance.ToBoundExpression(syntax);
     var fields = ArrayBuilder<FieldSymbol>.GetInstance();
     fields.AddRange(this.DisplayClassFields);
     fields.ReverseContents();
     foreach (var field in fields)
     {
         expr = new BoundFieldAccess(syntax, expr, field, constantValueOpt: null) { WasCompilerGenerated = true };
     }
     fields.Free();
     return expr;
 }
示例#3
0
        internal void Parse(BoundFieldAccess boundFieldAccess)
        {
            base.Parse(boundFieldAccess);
            this.Field = boundFieldAccess.FieldSymbol;
            if (boundFieldAccess.ReceiverOpt != null)
            {
                this.ReceiverOpt = Deserialize(boundFieldAccess.ReceiverOpt) as Expression;
            }

            var memberAccessExpressionSyntax = boundFieldAccess.Syntax as MemberAccessExpressionSyntax;

            if (memberAccessExpressionSyntax != null)
            {
                this.PointerAccess = memberAccessExpressionSyntax.Kind() == SyntaxKind.PointerMemberAccessExpression;
            }
        }
示例#4
0
        public override BoundNode VisitFieldAccess(BoundFieldAccess node)
        {
            BoundExpression receiverOpt = (BoundExpression)this.Visit(node.ReceiverOpt);
            TypeSymbol      type        = this.VisitType(node.Type);
            var             fieldSymbol = ((FieldSymbol)node.FieldSymbol.OriginalDefinition).AsMember(
                (NamedTypeSymbol)this.VisitType(node.FieldSymbol.ContainingType)
                );

            return(node.Update(
                       receiverOpt,
                       fieldSymbol,
                       node.ConstantValueOpt,
                       node.ResultKind,
                       type
                       ));
        }
示例#5
0
        internal BoundExpression ToBoundExpression(SyntaxNode syntax)
        {
            var expr   = this.DisplayClassInstance.ToBoundExpression(syntax);
            var fields = ArrayBuilder <FieldSymbol> .GetInstance();

            fields.AddRange(this.DisplayClassFields);
            fields.ReverseContents();
            foreach (var field in fields)
            {
                expr = new BoundFieldAccess(syntax, expr, field, constantValueOpt: null)
                {
                    WasCompilerGenerated = true
                };
            }
            fields.Free();
            return(expr);
        }
示例#6
0
        private LocalDefinition EmitInstanceFieldAddress(
            BoundFieldAccess fieldAccess,
            AddressKind addressKind
            )
        {
            var field = fieldAccess.FieldSymbol;

            //NOTE: we are not propagating AddressKind.Constrained here.
            //      the reason is that while Constrained permits calls, it does not permit
            //      taking field addresses, so we have to turn Constrained into writeable.
            var tempOpt = EmitReceiverRef(
                fieldAccess.ReceiverOpt,
                addressKind == AddressKind.Constrained ? AddressKind.Writeable : addressKind
                );

            _builder.EmitOpCode(ILOpCode.Ldflda);
            EmitSymbolToken(field, fieldAccess.Syntax);

            // when loading an address of a fixed field, we actually
            // want to load the address of its "FixedElementField" instead.
            // Both the buffer backing struct and its only field should be at the same location,
            // so we could in theory just use address of the struct, but in some contexts that causes
            // PEVerify errors because the struct has unexpected type. (Ex: struct& when int& is expected)
            if (field.IsFixedSizeBuffer)
            {
                var fixedImpl         = field.FixedImplementationType(_module);
                var fixedElementField = fixedImpl.FixedElementField;

                // if we get a mildly corrupted FixedImplementationType which does
                // not happen to have fixedElementField
                // we just leave address of the whole struct.
                //
                // That seems an adequate fallback because:
                // 1) it should happen only in impossibly rare cases involving malformed types
                // 2) the address of the struct is same as that of the buffer, just type is wrong.
                //    and that only matters to the verifier and we are in unsafe context anyways.
                if ((object)fixedElementField != null)
                {
                    _builder.EmitOpCode(ILOpCode.Ldflda);
                    EmitSymbolToken(fixedElementField, fieldAccess.Syntax);
                }
            }

            return(tempOpt);
        }
示例#7
0
        /// <summary>
        /// May introduce a temp which it will return. (otherwise returns null)
        /// </summary>
        private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess, AddressKind addressKind)
        {
            FieldSymbol field = fieldAccess.FieldSymbol;

            if (!HasHome(fieldAccess, addressKind))
            {
                // accessing a field that is not writable (const or readonly)
                return(EmitAddressOfTempClone(fieldAccess));
            }
            else if (fieldAccess.FieldSymbol.IsStatic)
            {
                EmitStaticFieldAddress(field, fieldAccess.Syntax);
                return(null);
            }
            else
            {
                return(EmitInstanceFieldAddress(fieldAccess, addressKind));
            }
        }
示例#8
0
        /// <summary>
        /// May introduce a temp which it will return. (otherwise returns null)
        /// </summary>
        private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess, AddressKind addressKind)
        {
            FieldSymbol field = fieldAccess.FieldSymbol;

            if (!HasHome(fieldAccess, addressKind != AddressKind.ReadOnly))
            {
                // accessing a field that is not writable (const or readonly)
                return(EmitAddressOfTempClone(fieldAccess));
            }
            else if (fieldAccess.FieldSymbol.IsStatic)
            {
                EmitStaticFieldAddress(field, fieldAccess.Syntax);
                return(null);
            }
            else
            {
                //NOTE: we are not propagating AddressKind here.
                //      the reason is that while Constrained permits calls, it does not permit
                //      taking field addresses, so we have to turn Constrained into writeable.
                //      It is less error prone to just pass a bool "isReadonly"
                return(EmitInstanceFieldAddress(fieldAccess, isReadonly: addressKind == AddressKind.ReadOnly));
            }
        }
示例#9
0
        /// <summary>
        /// Special HasHome for fields. Fields have homes when they are writable.
        /// </summary>
        private bool HasHome(BoundFieldAccess fieldAccess)
        {
            // Some field accesses must be values; values do not have homes.
            if (fieldAccess.IsByValue)
            {
                return(false);
            }

            FieldSymbol field = fieldAccess.FieldSymbol;

            // const fields are literal values with no homes
            if (field.IsConst)
            {
                return(false);
            }

            if (!field.IsReadOnly)
            {
                return(true);
            }

            // while readonly fields have home it is not valid to refer to it when not constructing.
            if (field.ContainingType != _method.ContainingType)
            {
                return(false);
            }

            if (field.IsStatic)
            {
                return(_method.MethodKind == MethodKind.StaticConstructor);
            }
            else
            {
                return(_method.MethodKind == MethodKind.Constructor &&
                       fieldAccess.ReceiverOpt.Kind == BoundKind.ThisReference);
            }
        }
示例#10
0
 public override BoundNode VisitFieldAccess(BoundFieldAccess node)
 {
     BoundExpression receiverOpt = (BoundExpression)this.Visit(node.ReceiverOpt);
     TypeSymbol type = this.VisitType(node.Type);
     var fieldSymbol = ((FieldSymbol)node.FieldSymbol.OriginalDefinition)
         .AsMember((NamedTypeSymbol)this.VisitType(node.FieldSymbol.ContainingType));
     return node.Update(receiverOpt, fieldSymbol, node.ConstantValueOpt, node.ResultKind, type);
 }
示例#11
0
        /// <summary>
        /// May introduce a temp which it will return. (otherwise returns null)
        /// </summary>
        private LocalDefinition EmitInstanceFieldAddress(BoundFieldAccess fieldAccess)
        {
            var field = fieldAccess.FieldSymbol;

            var tempOpt = EmitReceiverRef(fieldAccess.ReceiverOpt);

            builder.EmitOpCode(ILOpCode.Ldflda);
            EmitSymbolToken(field, fieldAccess.Syntax);

            // when loading an address of a fixed field, we actually 
            // want to load the address of its "FixedElementField" instead.
            // Both the buffer backing struct and its only field should be at the same location,
            // so we could in theory just use address of the struct, but in some contexts that causes 
            // PEVerify errors because the struct has unexpected type. (Ex: struct& when int& is expected)
            if (field.IsFixed)
            {
                var fixedImpl = field.FixedImplementationType(this.module);
                var fixedElementField = fixedImpl.FixedElementField;

                // if we get a mildly corrupted FixedImplementationType which does
                // not happen to have fixedElementField
                // we just leave address of the whole struct.
                //
                // That seems an adequate fallback because:
                // 1) it should happen only in impossibly rare cases involving malformed types
                // 2) the address of the struct is same as that of the buffer, just type is wrong.
                //    and that only matters to the verifier and we are in unsafe context anyways.
                if ((object)fixedElementField != null)
                {
                    builder.EmitOpCode(ILOpCode.Ldflda);
                    EmitSymbolToken(fixedElementField, fieldAccess.Syntax);
                }
            }

            return tempOpt;
        }
示例#12
0
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            if (node.HasErrors)
            {
                return(node);
            }

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

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

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

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

            BoundExpression transformedLHS = null;

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

                var prop = (BoundPropertyAccess)node.Left;

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

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

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

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

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

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

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

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

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

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

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

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

            BoundExpression rewrittenAssignment = (BoundExpression)Visit(assignment);

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

            temps.Free();
            stores.Free();
            return(result);
        }
示例#13
0
        /// <summary>
        /// May introduce a temp which it will return. (otherwise returns null)
        /// </summary>
        private LocalDefinition EmitFieldAddress(BoundFieldAccess fieldAccess)
        {
            FieldSymbol field = fieldAccess.FieldSymbol;

            if (!HasHome(fieldAccess))
            {
                // accessing a field that is not writeable (const or readonly)
                return EmitAddressOfTempClone(fieldAccess);
            }
            else if (fieldAccess.FieldSymbol.IsStatic)
            {
                EmitStaticFieldAddress(field, fieldAccess.Syntax);
                return null;
            }
            else
            {
                return EmitInstanceFieldAddress(fieldAccess);
            }
        }
示例#14
0
        /// <summary>
        /// Special HasHome for fields. Fields have homes when they are writeable.
        /// </summary>
        private bool HasHome(BoundFieldAccess fieldAccess)
        {
            FieldSymbol field = fieldAccess.FieldSymbol;

            // const fields are literal values with no homes
            if (field.IsConst)
            {
                return false;
            }

            if (!field.IsReadOnly)
            {
                return true;
            }

            // while readonly fields have home it is not valid to refer to it when not constructing.
            if (field.ContainingType != method.ContainingType)
            {
                return false;
            }

            if (field.IsStatic)
            {
                return method.MethodKind == MethodKind.StaticConstructor;
            }
            else
            {
                return method.MethodKind == MethodKind.Constructor &&
                    fieldAccess.ReceiverOpt.Kind == BoundKind.ThisReference;
            }
        }
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            if (node.HasErrors)
            {
                return node;
            }

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

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

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

            BoundExpression transformedLHS = null;

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

                var prop = (BoundPropertyAccess)node.Left;

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

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

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

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

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

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

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

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

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

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

            BoundExpression rewrittenAssignment = (BoundExpression)Visit(assignment);

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

            temps.Free();
            stores.Free();
            return result;
        }
示例#16
0
        private void EmitFieldLoad(BoundFieldAccess fieldAccess, bool used)
        {
            var field = fieldAccess.FieldSymbol;

            if (!used)
            {
                // fetching unused captured frame is a no-op (like reading "this")
                if (field.IsCapturedFrame)
                {
                    return;
                }

                // Accessing a volatile field is sideeffecting because it establishes an acquire fence.
                // Otherwise, accessing an unused instance field on a struct is a noop. Just emit an unused receiver.
                if (!field.IsVolatile && !field.IsStatic && fieldAccess.ReceiverOpt.Type.IsVerifierValue())
                {
                    EmitExpression(fieldAccess.ReceiverOpt, used: false);
                    return;
                }
            }

            Debug.Assert(!field.IsConst || field.ContainingType.SpecialType == SpecialType.System_Decimal,
                "rewriter should lower constant fields into constant expressions");

            // static field access is sideeffecting since it gurantees that ..ctor has run.
            // we emit static accesses even if unused.
            if (field.IsStatic)
            {
                if (field.IsVolatile)
                {
                    _builder.EmitOpCode(ILOpCode.Volatile);
                }
                _builder.EmitOpCode(ILOpCode.Ldsfld);
                EmitSymbolToken(field, fieldAccess.Syntax);
            }
            else
            {
                var receiver = fieldAccess.ReceiverOpt;
                var fieldType = field.Type;
                if (fieldType.IsValueType && (object)fieldType == (object)receiver.Type)
                {
                    //Handle emitting a field of a self-containing struct (only possible in mscorlib)
                    //since "val.field" is the same as val, we only need to emit val.
                    EmitExpression(receiver, used);
                }
                else
                {
                    var temp = EmitFieldLoadReceiver(receiver);
                    if (temp != null)
                    {
                        Debug.Assert(FieldLoadMustUseRef(receiver), "only clr-ambiguous structs use temps here");
                        FreeTemp(temp);
                    }

                    if (field.IsVolatile)
                    {
                        _builder.EmitOpCode(ILOpCode.Volatile);
                    }

                    _builder.EmitOpCode(ILOpCode.Ldfld);
                    EmitSymbolToken(field, fieldAccess.Syntax);
                }
            }
            EmitPopIfUnused(used);
        }
示例#17
0
        private void EmitFieldStore(BoundFieldAccess fieldAccess)
        {
            var field = fieldAccess.FieldSymbol;

            if (field.IsVolatile)
            {
                _builder.EmitOpCode(ILOpCode.Volatile);
            }

            _builder.EmitOpCode(field.IsStatic ? ILOpCode.Stsfld : ILOpCode.Stfld);
            EmitSymbolToken(field, fieldAccess.Syntax);
        }
示例#18
0
        public override BoundNode VisitFieldAccess(BoundFieldAccess node)
        {
            var field = node.FieldSymbol;
            var receiver = node.ReceiverOpt;

            // if there are any doubts that receiver is a ref type, 
            // assume we will need an address. (that will prevent scheduling of receiver).
            if (!field.IsStatic)
            {
                if (receiver.Type.IsTypeParameter())
                {
                    // type parameters must be boxed to access fields.
                    receiver = VisitExpression(receiver, ExprContext.Box);
                }
                else
                {
                    // need address when assigning to a field and receiver is not a reference
                    //              when accessing a field of a struct unless we only need Value and Value is preferred.
                    if (receiver.Type.IsValueType && (
                            _context == ExprContext.AssignmentTarget ||
                            _context == ExprContext.Address ||
                            CodeGenerator.FieldLoadMustUseRef(receiver)))
                    {
                        receiver = VisitExpression(receiver, ExprContext.Address);
                    }
                    else
                    {
                        receiver = VisitExpression(receiver, ExprContext.Value);
                    }
                }
            }
            else
            {
                // for some reason it could be not null even if field is static...
                //       it seems wrong
                _counter += 1;
                receiver = null;
            }

            return node.Update(receiver, field, node.ConstantValueOpt, node.ResultKind, node.Type);
        }
示例#19
0
        /// <summary>
        /// Special HasHome for fields.
        /// Fields have readable homes when they are not constants.
        /// Fields have writeable homes unless they are readonly and used outside of the constructor.
        /// </summary>
        private bool HasHome(BoundFieldAccess fieldAccess, AddressKind addressKind)
        {
            FieldSymbol field = fieldAccess.FieldSymbol;

            // const fields are literal values with no homes. (ex: decimal.Zero)
            if (field.IsConst)
            {
                return(false);
            }

            // in readonly situations where ref to a copy is not allowed, consider fields as addressable
            if (addressKind == AddressKind.ReadOnlyStrict)
            {
                return(true);
            }

            // ReadOnly references can always be taken unless we are in peverify compat mode
            if (addressKind == AddressKind.ReadOnly && !EnablePEVerifyCompat())
            {
                return(true);
            }

            // Some field accesses must be values; values do not have homes.
            if (fieldAccess.IsByValue)
            {
                return(false);
            }

            if (!field.IsReadOnly)
            {
                // in a case if we have a writeable struct field with a receiver that only has a readable home we would need to pass it via a temp.
                // it would be advantageous to make a temp for the field, not for the the outer struct, since the field is smaller and we can get to is by fetching references.
                // NOTE: this would not be profitable if we have to satisfy verifier, since for verifiability
                //       we would not be able to dig for the inner field using references and the outer struct will have to be copied to a temp anyways.
                if (!EnablePEVerifyCompat())
                {
                    Debug.Assert(!IsReadOnly(addressKind));

                    var receiver = fieldAccess.ReceiverOpt;
                    if (receiver?.Type.IsValueType == true)
                    {
                        // Check receiver:
                        // has writeable home -> return true - the whole chain has writeable home (also a more common case)
                        // has readable home -> return false - we need to copy the field
                        // otherwise         -> return true  - the copy will be made at higher level so the leaf field can have writeable home

                        return(HasHome(receiver, addressKind) ||
                               !HasHome(receiver, AddressKind.ReadOnly));
                    }
                }

                return(true);
            }

            // while readonly fields have home it is not valid to refer to it when not constructing.
            if (field.ContainingType != _method.ContainingType)
            {
                return(false);
            }

            if (field.IsStatic)
            {
                return(_method.MethodKind == MethodKind.StaticConstructor);
            }
            else
            {
                return(_method.MethodKind == MethodKind.Constructor &&
                       fieldAccess.ReceiverOpt.Kind == BoundKind.ThisReference);
            }
        }
示例#20
0
        private void EmitFieldLoad(BoundFieldAccess fieldAccess, bool used)
        {
            var field = fieldAccess.FieldSymbol;

            //TODO: For static field access this may require ..ctor to run. Is this a side-effect?
            // Accessing unused instance field on a struct is a noop. Just emit the receiver.
            if (!used && !field.IsVolatile && !field.IsStatic && fieldAccess.ReceiverOpt.Type.IsVerifierValue())
            {
                EmitExpression(fieldAccess.ReceiverOpt, used: false);
                return;
            }

            Debug.Assert(!field.IsConst || field.ContainingType.SpecialType == SpecialType.System_Decimal,
                "rewriter should lower constant fields into constant expressions");

            if (field.IsStatic)
            {
                if (field.IsVolatile)
                {
                    _builder.EmitOpCode(ILOpCode.Volatile);
                }
                _builder.EmitOpCode(ILOpCode.Ldsfld);
                EmitSymbolToken(field, fieldAccess.Syntax);
            }
            else
            {
                var receiver = fieldAccess.ReceiverOpt;
                var fieldType = field.Type;
                if (fieldType.IsValueType && (object)fieldType == (object)receiver.Type)
                {
                    //Handle emitting a field of a self-containing struct (only possible in mscorlib)
                    //since "val.field" is the same as val, we only need to emit val.
                    EmitExpression(receiver, used);
                }
                else
                {
                    var temp = EmitFieldLoadReceiver(receiver);
                    if (temp != null)
                    {
                        Debug.Assert(FieldLoadMustUseRef(receiver), "only clr-ambiguous structs use temps here");
                        FreeTemp(temp);
                    }

                    if (field.IsVolatile)
                    {
                        _builder.EmitOpCode(ILOpCode.Volatile);
                    }

                    _builder.EmitOpCode(ILOpCode.Ldfld);
                    EmitSymbolToken(field, fieldAccess.Syntax);
                }
            }
            EmitPopIfUnused(used);
        }
示例#21
0
 public override object VisitFieldAccess(BoundFieldAccess node, object arg)
 {
     VisitExpression(node.Receiver);
     // TODO: special case for instance fields of structs
     return(null);
 }