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