private BoundExpression MakeIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { if (node.OperatorKind.IsDynamic()) { return(_dynamicFactory.MakeDynamicUnaryOperator(node.OperatorKind, rewrittenValueToIncrement, node.Type).ToExpression()); } BoundExpression result; if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.UserDefined) { result = MakeUserDefinedIncrementOperator(node, rewrittenValueToIncrement); } else { result = MakeBuiltInIncrementOperator(node, rewrittenValueToIncrement); } // Generate the conversion back to the type of the original expression. // (X)(short)((int)(short)x + 1) if (!node.ResultConversion.IsIdentity) { result = MakeConversionNode( syntax: node.Syntax, rewrittenOperand: result, conversion: node.ResultConversion, rewrittenType: node.Type, @checked: node.OperatorKind.IsChecked()); } return(result); }
public override BoundNode VisitIncrementOperator(BoundIncrementOperator node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node); } return(base.VisitIncrementOperator(node)); }
private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { BoundExpression result; // If we have a built-in increment or decrement then things get a bit trickier. Suppose for example we have // a user-defined conversion from X to short and from short to X, but no user-defined increment operator on // X. The increment portion of "++x" is then: (X)(short)((int)(short)x + 1). That is, first x must be // converted to short via an implicit user- defined conversion, then to int via an implicit numeric // conversion, then the addition is performed in integers. The resulting integer is converted back to short, // and then the short is converted to X. // This is the input and output type of the unary increment operator we're going to call. // That is, "short" in the example above. TypeSymbol unaryOperandType = GetUnaryOperatorType(node); // This is the kind of binary operator that we're going to realize the unary operator // as. That is, "int + int --> int" in the example above. BinaryOperatorKind binaryOperatorKind = GetCorrespondingBinaryOperator(node); binaryOperatorKind |= IsIncrement(node) ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; // The "1" in the example above. ConstantValue constantOne = GetConstantOneForBinOp(binaryOperatorKind); Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); // The input/output type of the binary operand. "int" in the example. TypeSymbol binaryOperandType = _compilation.GetSpecialType(constantOne.SpecialType); // 1 BoundExpression boundOne = MakeLiteral( syntax: node.Syntax, constantValue: constantOne, type: binaryOperandType); if (binaryOperatorKind.IsLifted()) { binaryOperandType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(binaryOperandType); MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor); boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, boundOne); } // Now we construct the other operand to the binary addition. We start with just plain "x". BoundExpression binaryOperand = rewrittenValueToIncrement; bool @checked = node.OperatorKind.IsChecked(); // If we need to make a conversion from the original operand type to the operand type of the // underlying increment operation, do it now. if (!node.OperandConversion.IsIdentity) { // (short)x binaryOperand = MakeConversionNode( syntax: node.Syntax, rewrittenOperand: binaryOperand, conversion: node.OperandConversion, rewrittenType: unaryOperandType, @checked: @checked); } // Early-out for pointer increment - we don't need to convert the operands to a common type. if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.Pointer) { Debug.Assert(binaryOperatorKind.OperandTypes() == BinaryOperatorKind.PointerAndInt); Debug.Assert(binaryOperand.Type.IsPointerType()); Debug.Assert(boundOne.Type.SpecialType == SpecialType.System_Int32); return(MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperand.Type, method: null)); } // If we need to make a conversion from the unary operator type to the binary operator type, // do it now. // (int)(short)x binaryOperand = MakeConversionNode(binaryOperand, binaryOperandType, @checked); // Perform the addition. // (int)(short)x + 1 BoundExpression binOp; if (unaryOperandType.SpecialType == SpecialType.System_Decimal) { binOp = MakeDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand); } else if (unaryOperandType.IsNullableType() && unaryOperandType.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal) { binOp = MakeLiftedDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand); } else { binOp = MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperandType, method: null); } // Generate the conversion back to the type of the unary operator. // (short)((int)(short)x + 1) result = MakeConversionNode(binOp, unaryOperandType, @checked); return(result); }
private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { Debug.Assert((object)node.MethodOpt != null); Debug.Assert(node.MethodOpt.ParameterCount == 1); bool isLifted = node.OperatorKind.IsLifted(); bool @checked = node.OperatorKind.IsChecked(); BoundExpression rewrittenArgument = rewrittenValueToIncrement; SyntaxNode syntax = node.Syntax; TypeSymbol type = node.MethodOpt.ParameterTypes[0]; if (isLifted) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); Debug.Assert(node.MethodOpt.ParameterTypes[0] == node.MethodOpt.ReturnType); } if (!node.OperandConversion.IsIdentity) { rewrittenArgument = MakeConversionNode( syntax: syntax, rewrittenOperand: rewrittenValueToIncrement, conversion: node.OperandConversion, rewrittenType: type, @checked: @checked); } if (!isLifted) { return(BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument)); } // S? temp = operand; // S? r = temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); // Unlike the other unary operators, we do not attempt to optimize nullable user-defined // increment or decrement. The operand is a variable (or property), and so we do not know if // it is always null/never null. BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenArgument, out tempAssignment); MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor); // temp.HasValue BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Increment(temp.GetValueOrDefault()) BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault); // new S?(op_Increment(temp.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); // default(S?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); return(new BoundSequence( syntax: syntax, locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment), value: conditionalExpression, type: type)); }
/// <summary> /// The rewrites are as follows: suppose the operand x is a variable of type X. The /// chosen increment/decrement operator is modelled as a static method on a type T, /// which takes a value of type T and returns the result of incrementing or decrementing /// that value. /// /// x++ /// X temp = x /// x = (X)(T.Increment((T)temp)) /// return temp /// x-- /// X temp = x /// x = (X)(T.Decrement((T)temp)) /// return temp /// ++x /// X temp = (X)(T.Increment((T)x)) /// x = temp /// return temp /// --x /// X temp = (X)(T.Decrement((T)x)) /// x = temp /// return temp /// /// Note: /// Dev11 implements dynamic prefix operators incorrectly. /// /// result = ++x.P is emitted as result = SetMember{"P"}(t, UnaryOperation{Inc}(GetMember{"P"}(x))) /// /// The difference is that Dev11 relies on SetMember returning the same value as it was given as an argument. /// Failing to do so changes the semantics of ++/-- operator which is undesirable. We emit the same pattern for /// both dynamic and static operators. /// /// For example, we might have a class X with user-defined implicit conversions /// to and from short, but no user-defined increment or decrement operators. We /// would bind x++ as "X temp = x; x = (X)(short)((int)(short)temp + 1); return temp;" /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <returns>A bound sequence that uses a temp to achieve the correct side effects and return value.</returns> public override BoundNode VisitIncrementOperator(BoundIncrementOperator node) { bool isPrefix = IsPrefix(node); bool isDynamic = node.OperatorKind.IsDynamic(); bool isChecked = node.OperatorKind.IsChecked(); ArrayBuilder <LocalSymbol> tempSymbols = ArrayBuilder <LocalSymbol> .GetInstance(); ArrayBuilder <BoundExpression> tempInitializers = ArrayBuilder <BoundExpression> .GetInstance(); SyntaxNode syntax = node.Syntax; // This will be filled in with the LHS that uses temporaries to prevent // double-evaluation of side effects. BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Operand, tempInitializers, tempSymbols, isDynamic); TypeSymbol operandType = transformedLHS.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); LocalSymbol tempSymbol = _factory.SynthesizedLocal(operandType); tempSymbols.Add(tempSymbol); // Not adding an entry to tempInitializers because the initial value depends on the case. BoundExpression boundTemp = new BoundLocal( syntax: syntax, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // prefix: (X)(T.Increment((T)operand))) // postfix: (X)(T.Increment((T)temp))) var newValue = MakeIncrementOperator(node, rewrittenValueToIncrement: (isPrefix ? MakeRValue(transformedLHS) : boundTemp)); // there are two strategies for completing the rewrite. // The reason is that indirect assignments read the target of the assignment before evaluating // of the assignment value and that may cause reads of operand and boundTemp to cross which // in turn would require one of them to be a real temp (not a stack local) // // To avoid this issue, in a case of ByRef operand, we perform a "nested sequence" rewrite. // // Ex: // Seq{..., operand = Seq{temp = operand + 1, temp}, ...} // instead of // Seq{.... temp = operand + 1, operand = temp, ...} // // Such rewrite will nest reads of boundTemp relative to reads of operand so both // operand and boundTemp could be optimizable (subject to all other conditions of course). // // In a case of the non-byref operand we use a single-sequence strategy as it results in shorter // overall life time of temps and as such more appropriate. (problem of crossed reads does not affect that case) // if (IsIndirectOrInstanceField(transformedLHS)) { return(RewriteWithRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue)); } else { return(RewriteWithNotRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue)); } }
private static bool IsPrefix(BoundIncrementOperator node) { var op = node.OperatorKind.Operator(); return(op == UnaryOperatorKind.PrefixIncrement || op == UnaryOperatorKind.PrefixDecrement); }
private static void GetSymbolsAndResultKind(BoundIncrementOperator increment, out bool isDynamic, ref LookupResultKind resultKind, ref ImmutableArray<Symbol> symbols) { UnaryOperatorKind operandType = increment.OperatorKind.OperandTypes(); isDynamic = increment.OperatorKind.IsDynamic(); if (operandType == 0 || operandType == UnaryOperatorKind.UserDefined || increment.ResultKind != LookupResultKind.Viable) { if (!isDynamic) { GetSymbolsAndResultKind(increment, increment.MethodOpt, increment.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind); } } else { Debug.Assert((object)increment.MethodOpt == null && increment.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty); UnaryOperatorKind op = increment.OperatorKind.Operator(); symbols = ImmutableArray.Create<Symbol>(new SynthesizedIntrinsicOperatorSymbol(increment.Operand.Type.StrippedType(), OperatorFacts.UnaryOperatorNameFromOperatorKind(op), increment.Type.StrippedType(), increment.OperatorKind.IsChecked())); resultKind = increment.ResultKind; } }
private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { BoundExpression result; // If we have a built-in increment or decrement then things get a bit trickier. Suppose for example we have // a user-defined conversion from X to short and from short to X, but no user-defined increment operator on // X. The increment portion of "++x" is then: (X)(short)((int)(short)x + 1). That is, first x must be // converted to short via an implicit user- defined conversion, then to int via an implicit numeric // conversion, then the addition is performed in integers. The resulting integer is converted back to short, // and then the short is converted to X. // This is the input and output type of the unary increment operator we're going to call. // That is, "short" in the example above. TypeSymbol unaryOperandType = GetUnaryOperatorType(node); // This is the kind of binary operator that we're going to realize the unary operator // as. That is, "int + int --> int" in the example above. BinaryOperatorKind binaryOperatorKind = GetCorrespondingBinaryOperator(node); binaryOperatorKind |= IsIncrement(node) ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; // The "1" in the example above. ConstantValue constantOne = GetConstantOneForBinOp(binaryOperatorKind); Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); // The input/output type of the binary operand. "int" in the example. TypeSymbol binaryOperandType = _compilation.GetSpecialType(constantOne.SpecialType); // 1 BoundExpression boundOne = MakeLiteral( syntax: node.Syntax, constantValue: constantOne, type: binaryOperandType); if (binaryOperatorKind.IsLifted()) { binaryOperandType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(binaryOperandType); MethodSymbol ctor = GetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor); boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, boundOne); } // Now we construct the other operand to the binary addition. We start with just plain "x". BoundExpression binaryOperand = rewrittenValueToIncrement; bool @checked = node.OperatorKind.IsChecked(); // If we need to make a conversion from the original operand type to the operand type of the // underlying increment operation, do it now. if (!node.OperandConversion.IsIdentity) { // (short)x binaryOperand = MakeConversionNode( syntax: node.Syntax, rewrittenOperand: binaryOperand, conversion: node.OperandConversion, rewrittenType: unaryOperandType, @checked: @checked); } // Early-out for pointer increment - we don't need to convert the operands to a common type. if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.Pointer) { Debug.Assert(binaryOperatorKind.OperandTypes() == BinaryOperatorKind.PointerAndInt); Debug.Assert(binaryOperand.Type.IsPointerType()); Debug.Assert(boundOne.Type.SpecialType == SpecialType.System_Int32); return MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperand.Type, method: null); } // If we need to make a conversion from the unary operator type to the binary operator type, // do it now. // (int)(short)x binaryOperand = MakeConversionNode(binaryOperand, binaryOperandType, @checked); // Perform the addition. // (int)(short)x + 1 BoundExpression binOp; if (unaryOperandType.SpecialType == SpecialType.System_Decimal) { binOp = MakeDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand); } else if (unaryOperandType.IsNullableType() && unaryOperandType.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal) { binOp = MakeLiftedDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand); } else { binOp = MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperandType, method: null); } // Generate the conversion back to the type of the unary operator. // (short)((int)(short)x + 1) result = MakeConversionNode(binOp, unaryOperandType, @checked); return result; }
// There are ++ and -- operators defined on sbyte, byte, short, ushort, int, // uint, long, ulong, char, float, double, decimal and any enum type. // Given a built-in increment operator, get the associated type. Note // that this need not be the result type or the operand type of the node! // We could have a user-defined conversion from the type of the operand // to short, and a user-defined conversion from short to the result // type. private TypeSymbol GetUnaryOperatorType(BoundIncrementOperator node) { UnaryOperatorKind kind = node.OperatorKind.OperandTypes(); // If overload resolution chose an enum operator then the operand // type and the return type really are an enum; we are not in a user- // defined conversion scenario. if (kind == UnaryOperatorKind.Enum) { return node.Type; } SpecialType specialType; switch (kind) { case UnaryOperatorKind.Int: specialType = SpecialType.System_Int32; break; case UnaryOperatorKind.SByte: specialType = SpecialType.System_SByte; break; case UnaryOperatorKind.Short: specialType = SpecialType.System_Int16; break; case UnaryOperatorKind.Byte: specialType = SpecialType.System_Byte; break; case UnaryOperatorKind.UShort: specialType = SpecialType.System_UInt16; break; case UnaryOperatorKind.Char: specialType = SpecialType.System_Char; break; case UnaryOperatorKind.UInt: specialType = SpecialType.System_UInt32; break; case UnaryOperatorKind.Long: specialType = SpecialType.System_Int64; break; case UnaryOperatorKind.ULong: specialType = SpecialType.System_UInt64; break; case UnaryOperatorKind.Float: specialType = SpecialType.System_Single; break; case UnaryOperatorKind.Double: specialType = SpecialType.System_Double; break; case UnaryOperatorKind.Decimal: specialType = SpecialType.System_Decimal; break; case UnaryOperatorKind.Pointer: return node.Type; case UnaryOperatorKind.UserDefined: case UnaryOperatorKind.Bool: default: throw ExceptionUtilities.UnexpectedValue(kind); } NamedTypeSymbol type = _compilation.GetSpecialType(specialType); if (node.OperatorKind.IsLifted()) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); } return type; }
private BoundExpression MakeIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { if (node.OperatorKind.IsDynamic()) { return _dynamicFactory.MakeDynamicUnaryOperator(node.OperatorKind, rewrittenValueToIncrement, node.Type).ToExpression(); } BoundExpression result; if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.UserDefined) { result = MakeUserDefinedIncrementOperator(node, rewrittenValueToIncrement); } else { result = MakeBuiltInIncrementOperator(node, rewrittenValueToIncrement); } // Generate the conversion back to the type of the original expression. // (X)(short)((int)(short)x + 1) if (!node.ResultConversion.IsIdentity) { result = MakeConversionNode( syntax: node.Syntax, rewrittenOperand: result, conversion: node.ResultConversion, rewrittenType: node.Type, @checked: node.OperatorKind.IsChecked()); } return result; }
private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement) { Debug.Assert((object)node.MethodOpt != null); Debug.Assert(node.MethodOpt.ParameterCount == 1); bool isLifted = node.OperatorKind.IsLifted(); bool @checked = node.OperatorKind.IsChecked(); BoundExpression rewrittenArgument = rewrittenValueToIncrement; CSharpSyntaxNode syntax = node.Syntax; TypeSymbol type = node.MethodOpt.ParameterTypes[0]; if (isLifted) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); Debug.Assert(node.MethodOpt.ParameterTypes[0] == node.MethodOpt.ReturnType); } if (!node.OperandConversion.IsIdentity) { rewrittenArgument = MakeConversionNode( syntax: syntax, rewrittenOperand: rewrittenValueToIncrement, conversion: node.OperandConversion, rewrittenType: type, @checked: @checked); } if (!isLifted) { return BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument); } // S? temp = operand; // S? r = temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); // Unlike the other unary operators, we do not attempt to optimize nullable user-defined // increment or decrement. The operand is a variable (or property), and so we do not know if // it is always null/never null. BoundAssignmentOperator tempAssignment; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenArgument, out tempAssignment); MethodSymbol getValueOrDefault = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault); MethodSymbol ctor = GetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor); // temp.HasValue BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp); // temp.GetValueOrDefault() BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); // op_Increment(temp.GetValueOrDefault()) BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault); // new S?(op_Increment(temp.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); // default(S?) BoundExpression alternative = new BoundDefaultOperator(syntax, null, type); // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); BoundExpression conditionalExpression = RewriteConditionalOperator( syntax: syntax, rewrittenCondition: condition, rewrittenConsequence: consequence, rewrittenAlternative: alternative, constantValueOpt: null, rewrittenType: type); // temp = operand; // temp.HasValue ? // new S?(op_Increment(temp.GetValueOrDefault())) : // default(S?); return new BoundSequence( syntax: syntax, locals: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment), value: conditionalExpression, type: type); }
/// <summary> /// The rewrites are as follows: suppose the operand x is a variable of type X. The /// chosen increment/decrement operator is modelled as a static method on a type T, /// which takes a value of type T and returns the result of incrementing or decrementing /// that value. /// /// x++ /// X temp = x /// x = (X)(T.Increment((T)temp)) /// return temp /// x-- /// X temp = x /// x = (X)(T.Decrement((T)temp)) /// return temp /// ++x /// X temp = (X)(T.Increment((T)x)) /// x = temp /// return temp /// --x /// X temp = (X)(T.Decrement((T)x)) /// x = temp /// return temp /// /// Note: /// Dev11 implements dynamic prefix operators incorrectly. /// /// result = ++x.P is emitted as result = SetMember{"P"}(t, UnaryOperation{Inc}(GetMember{"P"}(x))) /// /// The difference is that Dev11 relies on SetMember returning the same value as it was given as an argument. /// Failing to do so changes the semantics of ++/-- operator which is undesirable. We emit the same pattern for /// both dynamic and static operators. /// /// For example, we might have a class X with user-defined implicit conversions /// to and from short, but no user-defined increment or decrement operators. We /// would bind x++ as "X temp = x; x = (X)(short)((int)(short)temp + 1); return temp;" /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <returns>A bound sequence that uses a temp to achieve the correct side effects and return value.</returns> public override BoundNode VisitIncrementOperator(BoundIncrementOperator node) { bool isPrefix = IsPrefix(node); bool isDynamic = node.OperatorKind.IsDynamic(); bool isChecked = node.OperatorKind.IsChecked(); ArrayBuilder<LocalSymbol> tempSymbols = ArrayBuilder<LocalSymbol>.GetInstance(); ArrayBuilder<BoundExpression> tempInitializers = ArrayBuilder<BoundExpression>.GetInstance(); CSharpSyntaxNode syntax = node.Syntax; // This will be filled in with the LHS that uses temporaries to prevent // double-evaluation of side effects. BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Operand, tempInitializers, tempSymbols, isDynamic); TypeSymbol operandType = transformedLHS.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); LocalSymbol tempSymbol = _factory.SynthesizedLocal(operandType); tempSymbols.Add(tempSymbol); // Not adding an entry to tempInitializers because the initial value depends on the case. BoundExpression boundTemp = new BoundLocal( syntax: syntax, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // prefix: (X)(T.Increment((T)operand))) // postfix: (X)(T.Increment((T)temp))) var newValue = MakeIncrementOperator(node, rewrittenValueToIncrement: (isPrefix ? MakeRValue(transformedLHS) : boundTemp)); // there are two strategies for completing the rewrite. // The reason is that indirect assignments read the target of the assignment before evaluating // of the assignment value and that may cause reads of operand and boundTemp to cross which // in turn would require one of them to be a real temp (not a stack local) // // To avoid this issue, in a case of ByRef operand, we perform a "nested sequence" rewrite. // // Ex: // Seq{..., operand = Seq{temp = operand + 1, temp}, ...} // instead of // Seq{.... temp = operand + 1, operand = temp, ...} // // Such rewrite will nest reads of boundTemp relative to reads of operand so both // operand and boundTemp could be optimizable (subject to all other conditions of course). // // In a case of the non-byref operand we use a single-sequence strategy as it results in shorter // overall life time of temps and as such more appropriate. (problem of crossed reads does not affect that case) // if (IsIndirectOrInstanceField(transformedLHS)) { return RewriteWithRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue); } else { return RewriteWithNotRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue); } }
private static bool IsPrefix(BoundIncrementOperator node) { var op = node.OperatorKind.Operator(); return op == UnaryOperatorKind.PrefixIncrement || op == UnaryOperatorKind.PrefixDecrement; }
public override BoundNode?VisitIncrementOperator(BoundIncrementOperator node) { _mightAssignSomething = true; return(null); }
// There are ++ and -- operators defined on sbyte, byte, short, ushort, int, // uint, long, ulong, char, float, double, decimal and any enum type. // Given a built-in increment operator, get the associated type. Note // that this need not be the result type or the operand type of the node! // We could have a user-defined conversion from the type of the operand // to short, and a user-defined conversion from short to the result // type. private TypeSymbol GetUnaryOperatorType(BoundIncrementOperator node) { UnaryOperatorKind kind = node.OperatorKind.OperandTypes(); // If overload resolution chose an enum operator then the operand // type and the return type really are an enum; we are not in a user- // defined conversion scenario. if (kind == UnaryOperatorKind.Enum) { return(node.Type); } SpecialType specialType; switch (kind) { case UnaryOperatorKind.Int: specialType = SpecialType.System_Int32; break; case UnaryOperatorKind.SByte: specialType = SpecialType.System_SByte; break; case UnaryOperatorKind.Short: specialType = SpecialType.System_Int16; break; case UnaryOperatorKind.Byte: specialType = SpecialType.System_Byte; break; case UnaryOperatorKind.UShort: specialType = SpecialType.System_UInt16; break; case UnaryOperatorKind.Char: specialType = SpecialType.System_Char; break; case UnaryOperatorKind.UInt: specialType = SpecialType.System_UInt32; break; case UnaryOperatorKind.Long: specialType = SpecialType.System_Int64; break; case UnaryOperatorKind.ULong: specialType = SpecialType.System_UInt64; break; case UnaryOperatorKind.Float: specialType = SpecialType.System_Single; break; case UnaryOperatorKind.Double: specialType = SpecialType.System_Double; break; case UnaryOperatorKind.Decimal: specialType = SpecialType.System_Decimal; break; case UnaryOperatorKind.Pointer: return(node.Type); case UnaryOperatorKind.UserDefined: case UnaryOperatorKind.Bool: default: throw ExceptionUtilities.UnexpectedValue(kind); } NamedTypeSymbol type = _compilation.GetSpecialType(specialType); if (node.OperatorKind.IsLifted()) { type = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); } return(type); }
private static BinaryOperatorKind GetCorrespondingBinaryOperator(BoundIncrementOperator node) { // We need to create expressions that have the semantics of incrementing or decrementing: // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal and // any enum. However, the binary addition operators we have at our disposal are just // int, uint, long, ulong, float, double and decimal. UnaryOperatorKind unaryOperatorKind = node.OperatorKind; BinaryOperatorKind result; switch (unaryOperatorKind.OperandTypes()) { case UnaryOperatorKind.Int: case UnaryOperatorKind.SByte: case UnaryOperatorKind.Short: result = BinaryOperatorKind.Int; break; case UnaryOperatorKind.Byte: case UnaryOperatorKind.UShort: case UnaryOperatorKind.Char: case UnaryOperatorKind.UInt: result = BinaryOperatorKind.UInt; break; case UnaryOperatorKind.Long: result = BinaryOperatorKind.Long; break; case UnaryOperatorKind.ULong: result = BinaryOperatorKind.ULong; break; case UnaryOperatorKind.Float: result = BinaryOperatorKind.Float; break; case UnaryOperatorKind.Double: result = BinaryOperatorKind.Double; break; case UnaryOperatorKind.Decimal: //Dev10 special cased this, but we'll let DecimalRewriter handle it result = BinaryOperatorKind.Decimal; break; case UnaryOperatorKind.Enum: { TypeSymbol underlyingType = node.Type; if (underlyingType.IsNullableType()) { underlyingType = underlyingType.GetNullableUnderlyingType(); } Debug.Assert(underlyingType.IsEnumType()); underlyingType = underlyingType.GetEnumUnderlyingType(); // Operator overload resolution will not have chosen the enumerated type // unless the operand actually is of the enumerated type (or nullable enum type.) switch (underlyingType.SpecialType) { case SpecialType.System_SByte: case SpecialType.System_Int16: case SpecialType.System_Int32: result = BinaryOperatorKind.Int; break; case SpecialType.System_Byte: case SpecialType.System_UInt16: case SpecialType.System_UInt32: result = BinaryOperatorKind.UInt; break; case SpecialType.System_Int64: result = BinaryOperatorKind.Long; break; case SpecialType.System_UInt64: result = BinaryOperatorKind.ULong; break; default: throw ExceptionUtilities.UnexpectedValue(underlyingType.SpecialType); } } break; case UnaryOperatorKind.Pointer: result = BinaryOperatorKind.PointerAndInt; break; case UnaryOperatorKind.UserDefined: case UnaryOperatorKind.Bool: default: throw ExceptionUtilities.UnexpectedValue(unaryOperatorKind.OperandTypes()); } switch (result) { case BinaryOperatorKind.UInt: case BinaryOperatorKind.Int: case BinaryOperatorKind.ULong: case BinaryOperatorKind.Long: case BinaryOperatorKind.PointerAndInt: result |= (BinaryOperatorKind)unaryOperatorKind.OverflowChecks(); break; } if (unaryOperatorKind.IsLifted()) { result |= BinaryOperatorKind.Lifted; } return result; }
private static BinaryOperatorKind GetCorrespondingBinaryOperator(BoundIncrementOperator node) { // We need to create expressions that have the semantics of incrementing or decrementing: // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal and // any enum. However, the binary addition operators we have at our disposal are just // int, uint, long, ulong, float, double and decimal. UnaryOperatorKind unaryOperatorKind = node.OperatorKind; BinaryOperatorKind result; switch (unaryOperatorKind.OperandTypes()) { case UnaryOperatorKind.Int: case UnaryOperatorKind.SByte: case UnaryOperatorKind.Short: result = BinaryOperatorKind.Int; break; case UnaryOperatorKind.Byte: case UnaryOperatorKind.UShort: case UnaryOperatorKind.Char: case UnaryOperatorKind.UInt: result = BinaryOperatorKind.UInt; break; case UnaryOperatorKind.Long: result = BinaryOperatorKind.Long; break; case UnaryOperatorKind.ULong: result = BinaryOperatorKind.ULong; break; case UnaryOperatorKind.Float: result = BinaryOperatorKind.Float; break; case UnaryOperatorKind.Double: result = BinaryOperatorKind.Double; break; case UnaryOperatorKind.Decimal: //Dev10 special cased this, but we'll let DecimalRewriter handle it result = BinaryOperatorKind.Decimal; break; case UnaryOperatorKind.Enum: { TypeSymbol underlyingType = node.Type; if (underlyingType.IsNullableType()) { underlyingType = underlyingType.GetNullableUnderlyingType(); } Debug.Assert(underlyingType.IsEnumType()); underlyingType = underlyingType.GetEnumUnderlyingType(); // Operator overload resolution will not have chosen the enumerated type // unless the operand actually is of the enumerated type (or nullable enum type.) switch (underlyingType.SpecialType) { case SpecialType.System_SByte: case SpecialType.System_Int16: case SpecialType.System_Int32: result = BinaryOperatorKind.Int; break; case SpecialType.System_Byte: case SpecialType.System_UInt16: case SpecialType.System_UInt32: result = BinaryOperatorKind.UInt; break; case SpecialType.System_Int64: result = BinaryOperatorKind.Long; break; case SpecialType.System_UInt64: result = BinaryOperatorKind.ULong; break; default: throw ExceptionUtilities.UnexpectedValue(underlyingType.SpecialType); } } break; case UnaryOperatorKind.Pointer: result = BinaryOperatorKind.PointerAndInt; break; case UnaryOperatorKind.UserDefined: case UnaryOperatorKind.Bool: default: throw ExceptionUtilities.UnexpectedValue(unaryOperatorKind.OperandTypes()); } switch (result) { case BinaryOperatorKind.UInt: case BinaryOperatorKind.Int: case BinaryOperatorKind.ULong: case BinaryOperatorKind.Long: case BinaryOperatorKind.PointerAndInt: result |= (BinaryOperatorKind)unaryOperatorKind.OverflowChecks(); break; } if (unaryOperatorKind.IsLifted()) { result |= BinaryOperatorKind.Lifted; } return(result); }
public override BoundNode VisitIncrementOperator(BoundIncrementOperator node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node); } return base.VisitIncrementOperator(node); }