Example #1
0
        /// <summary>
        /// Rewrites arguments of an invocation according to the receiving method. It is assumed
        /// that arguments match parameters, but may need to be expanded/reordered.
        /// </summary>
        private void RewriteArguments(
            MethodSymbol method,
            bool expanded,
            ReadOnlyArray <int> argsToParamsOpt,
            ref ReadOnlyArray <RefKind> argumentRefKinds,
            ref ReadOnlyArray <BoundExpression> rewrittenArguments,
            out ReadOnlyArray <LocalSymbol> temporaries)
        {
            // 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.

            ReadOnlyArray <ParameterSymbol> parameters = method.Parameters;
            var parameterCount = parameters.Count;
            var arguments      = new BoundExpression[parameterCount];

            temporaries = ReadOnlyArray <LocalSymbol> .Null;  // not using temps by default.

            List <RefKind> refKinds = null;

            if (argumentRefKinds.IsNotNull)
            {
                refKinds = new List <RefKind>(parameterCount);
                for (int p = 0; p < parameterCount; ++p)
                {
                    refKinds.Add(RefKind.None);
                }
            }

            ArrayBuilder <BoundAssignmentOperator> storesToTemps = null;
            ArrayBuilder <BoundExpression>         paramArray    = null;

            if (expanded)
            {
                paramArray = ArrayBuilder <BoundExpression> .GetInstance();
            }

            for (int a = 0; a < rewrittenArguments.Count; ++a)
            {
                var argument = rewrittenArguments[a];
                var p        = (argsToParamsOpt.IsNotNull) ? argsToParamsOpt[a] : a;
                var refKind  = argumentRefKinds.RefKinds(a);
                Debug.Assert(arguments[p] == null);
                if (expanded && p == parameterCount - 1)
                {
                    paramArray.Add(argument);
                    Debug.Assert(refKind == RefKind.None);
                }
                else if (IsSafeForReordering(argument, refKind))
                {
                    arguments[p] = argument;
                    if (refKinds != null)
                    {
                        refKinds[p] = refKind;
                    }
                }
                else
                {
                    if (storesToTemps == null)
                    {
                        storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Count);
                    }

                    var tempStore = TempHelpers.StoreToTemp(argument, refKind, containingSymbol);
                    storesToTemps.Add(tempStore.Item1);
                    arguments[p] = tempStore.Item2;
                }
            }

            if (expanded)
            {
                var paramArrayType = parameters[parameterCount - 1].Type;
                var arrayArgs      = paramArray.ToReadOnlyAndFree();

                var int32Type = method.ContainingAssembly.GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode.Int32);

                arguments[parameterCount - 1] = new BoundArrayCreation(
                    null,
                    null,
                    ReadOnlyArray.Singleton <BoundExpression>(
                        new BoundLiteral(null, null, ConstantValue.Create(arrayArgs.Count), int32Type)),
                    new BoundArrayInitialization(null, null, arrayArgs),
                    paramArrayType);
            }

            for (int p = 0; p < parameterCount; ++p)
            {
                if (arguments[p] == null)
                {
                    Debug.Assert(parameters[p].IsOptional);

                    // UNDONE: Add optional arguments.
                }
            }

            if (storesToTemps != null)
            {
                int tempsNeeded = MergeArgumentsAndSideEffects(storesToTemps, arguments);

                if (tempsNeeded > 0)
                {
                    var temps = new LocalSymbol[tempsNeeded];
                    for (int i = 0, j = 0; i < storesToTemps.Count; i++)
                    {
                        var s = storesToTemps[i];
                        if (s != null)
                        {
                            temps[j++] = ((BoundLocal)s.Left).LocalSymbol;
                        }
                    }

                    temporaries = temps.AsReadOnlyWrap();
                }

                storesToTemps.Free();
            }

            // * The rewritten list of 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.

            argumentRefKinds   = refKinds == null ? ReadOnlyArray <RefKind> .Null : refKinds.AsReadOnly <RefKind>();
            rewrittenArguments = arguments.AsReadOnlyWrap();
        }
        internal SemanticInfo(
            TypeSymbol type,
            Conversion conversion,
            TypeSymbol convertedType,
            ReadOnlyArray<Symbol> symbols,
            LookupResultKind resultKind,
            ReadOnlyArray<MethodSymbol> methodGroup,
            ConstantValue constantValue)
        {
            // When constructing the result for the Caas API, we expose the underlying symbols that
            // may have been hidden under error type, if the error type was immediate. We will
            // expose error types that were constructed, or type parameters of constructed types.
            this.Type = type.GetNonErrorGuess() ?? type;
            this.ConvertedType = convertedType.GetNonErrorGuess() ?? convertedType;
            this.ImplicitConversion = conversion;

            this.symbols = symbols;
            this.resultKind = resultKind;
            if (!symbols.Any())
            {
                this.resultKind = LookupResultKind.Empty;
            }

            this.MethodGroup = methodGroup;
            this.constantValue = constantValue;
        }
Example #3
0
 public EvaluatedConstant(ConstantValue value, IEnumerable<IDiagnostic> diagnostics)
 {
     this.Value = value;
     this.Diagnostics = diagnostics;
 }
 /// <summary>
 /// By examining the node and the UnaryOperatorKind detemine which builtin operator should be used
 /// and create an appropriately-typed constant 1.
 /// </summary>
 /// <param name="unaryOperatorKindType">The operand type of the built-in increment/decrement operator.</param>
 /// <param name="node">The unary operation - used to extract an underlying enum type, if necessary.</param>
 /// <param name="constantOne">Will contain a constant of the type expected by the built-in operator corresponding to binaryOperatorKindType.</param>
 /// <param name="binaryOperatorKindType">The built-in binary operator that will be used to implement the built-in increment/decrement operator.  May have wider types.</param>
 private static void MakeConstantAndOperatorKind(UnaryOperatorKind unaryOperatorKindType, BoundUnaryOperator node, out ConstantValue constantOne, out BinaryOperatorKind binaryOperatorKindType)
 {
     switch (unaryOperatorKindType)
     {
         case UnaryOperatorKind.SByte:
         case UnaryOperatorKind.Short:
         case UnaryOperatorKind.Int:
             constantOne = ConstantValue.ConstantValueOne.Int32;
             binaryOperatorKindType = BinaryOperatorKind.Int;
             break;
         case UnaryOperatorKind.Byte:
         case UnaryOperatorKind.UShort:
         case UnaryOperatorKind.UInt:
         case UnaryOperatorKind.Char:
             constantOne = ConstantValue.ConstantValueOne.UInt32;
             binaryOperatorKindType = BinaryOperatorKind.UInt;
             break;
         case UnaryOperatorKind.Long:
             constantOne = ConstantValue.ConstantValueOne.Int64;
             binaryOperatorKindType = BinaryOperatorKind.Long;
             break;
         case UnaryOperatorKind.ULong:
             constantOne = ConstantValue.ConstantValueOne.UInt64;
             binaryOperatorKindType = BinaryOperatorKind.ULong;
             break;
         case UnaryOperatorKind.Float:
             constantOne = ConstantValue.ConstantValueOne.Single;
             binaryOperatorKindType = BinaryOperatorKind.Float;
             break;
         case UnaryOperatorKind.Double:
             constantOne = ConstantValue.ConstantValueOne.Double;
             binaryOperatorKindType = BinaryOperatorKind.Double;
             break;
         case UnaryOperatorKind.Decimal: //Dev10 special cased this, but we'll let DecimalRewriter handle it
             constantOne = ConstantValue.ConstantValueOne.Decimal;
             binaryOperatorKindType = BinaryOperatorKind.Decimal;
             break;
         case UnaryOperatorKind.Enum:
             SpecialType underlyingSpecialType = node.Type.GetEnumUnderlyingType().SpecialType;
             switch (underlyingSpecialType)
             {
                 case SpecialType.System_Int32: MakeConstantAndOperatorKind(UnaryOperatorKind.Int, node, out constantOne, out binaryOperatorKindType); return;
                 case SpecialType.System_UInt32: MakeConstantAndOperatorKind(UnaryOperatorKind.UInt, node, out constantOne, out binaryOperatorKindType); return;
                 case SpecialType.System_Int64: MakeConstantAndOperatorKind(UnaryOperatorKind.Long, node, out constantOne, out binaryOperatorKindType); return;
                 case SpecialType.System_UInt64: MakeConstantAndOperatorKind(UnaryOperatorKind.ULong, node, out constantOne, out binaryOperatorKindType); return;
                 case SpecialType.System_SByte: MakeConstantAndOperatorKind(UnaryOperatorKind.SByte, node, out constantOne, out binaryOperatorKindType); return;
                 case SpecialType.System_Byte: MakeConstantAndOperatorKind(UnaryOperatorKind.Byte, node, out constantOne, out binaryOperatorKindType); return;
                 case SpecialType.System_Int16: MakeConstantAndOperatorKind(UnaryOperatorKind.Short, node, out constantOne, out binaryOperatorKindType); return;
                 case SpecialType.System_UInt16: MakeConstantAndOperatorKind(UnaryOperatorKind.UShort, node, out constantOne, out binaryOperatorKindType); return;
                 default: throw new InvalidOperationException("Unexpected enum underlying type: " + underlyingSpecialType);
             }
         case UnaryOperatorKind.Pointer:
             //UNDONE: pointer operations
             throw new NotImplementedException();
         case UnaryOperatorKind.UserDefined:
             //UNDONE: overloaded increment/decrement operators
             throw new NotImplementedException();
         case UnaryOperatorKind.Bool:
             Debug.Assert(false); //Operator does not exist
             goto default;
         default:
             throw new InvalidOperationException("Unexpected operator type: " + unaryOperatorKindType);
     }
 }