public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryOperator applyParentUnaryOperator = null) { // In machine-generated code we frequently end up with binary operator trees that are deep on the left, // such as a + b + c + d ... // To avoid blowing the call stack, we make an explicit stack of the binary operators to the left, // and then lower by traversing the explicit stack. var stack = ArrayBuilder<BoundBinaryOperator>.GetInstance(); for (BoundBinaryOperator current = node; current != null; current = current.Left as BoundBinaryOperator) { stack.Push(current); } BoundExpression loweredLeft = VisitExpression(stack.Peek().Left); while (stack.Count > 0) { BoundBinaryOperator original = stack.Pop(); BoundExpression loweredRight = VisitExpression(original.Right); loweredLeft = MakeBinaryOperator(original, original.Syntax, original.OperatorKind, loweredLeft, loweredRight, original.Type, original.MethodOpt, applyParentUnaryOperator: (stack.Count == 0) ? applyParentUnaryOperator : null); } stack.Free(); return loweredLeft; }
private BoundExpression TryFoldWithConditionalAccess(BoundBinaryOperator node) { var syntax = node.Syntax; var left = node.Left; var right = node.Right; BoundConditionalAccess conditionalAccess; if (!node.OperatorKind.IsLifted()) { var operatorKind = node.OperatorKind; if (operatorKind == BinaryOperatorKind.NullableNullEqual || operatorKind == BinaryOperatorKind.NullableNullNotEqual) { var leftAlwaysNull = NullableNeverHasValue(left); var rightAlwaysNull = NullableNeverHasValue(right); if (leftAlwaysNull || rightAlwaysNull) { BoundExpression maybeNull = leftAlwaysNull ? right : left; if (TryGetOptimizableNullableConditionalAccess(maybeNull, out conditionalAccess)) { BoundExpression accessExpression = conditionalAccess.AccessExpression; accessExpression = _factory.Sequence(accessExpression, MakeBooleanConstant(syntax, operatorKind == BinaryOperatorKind.NullableNullNotEqual)); conditionalAccess = conditionalAccess.Update(conditionalAccess.Receiver, accessExpression, accessExpression.Type); var whenNull = operatorKind == BinaryOperatorKind.NullableNullEqual ? MakeBooleanConstant(syntax, true) : null; return RewriteConditionalAccess(conditionalAccess, used: true, rewrittenWhenNull: whenNull); } } } } else { var unliftedOperatorKind = node.OperatorKind.Unlifted(); if (unliftedOperatorKind.IsComparison() && TryGetOptimizableNullableConditionalAccess(left, out conditionalAccess)) { var rightAlwaysHasValue = NullableAlwaysHasValue(VisitExpression(right)); // reading rightAlwaysHasValue should not have sideeffects here // otherwise we would need to read it even when we knew that LHS is null if (rightAlwaysHasValue != null && !IntroducingReadCanBeObservable(rightAlwaysHasValue)) { BoundExpression accessExpression = conditionalAccess.AccessExpression; accessExpression = node.Update(unliftedOperatorKind, accessExpression, rightAlwaysHasValue, null, node.MethodOpt, node.ResultKind, node.Type); conditionalAccess = conditionalAccess.Update(conditionalAccess.Receiver, accessExpression, accessExpression.Type); var whenNull = unliftedOperatorKind.Operator() == BinaryOperatorKind.NotEqual ? MakeBooleanConstant(syntax, true) : null; return RewriteConditionalAccess(conditionalAccess, used: true, rewrittenWhenNull: whenNull); } } } return null; }
private BoundNode VisitBinaryOperatorBase(BoundBinaryOperatorBase binaryOperator) { // Use an explicit stack to avoid blowing the managed stack when visiting deeply-recursive // binary nodes var stack = ArrayBuilder <BoundBinaryOperatorBase> .GetInstance(); BoundBinaryOperatorBase?currentBinary = binaryOperator; do { stack.Push(currentBinary); currentBinary = currentBinary.Left as BoundBinaryOperatorBase; }while (currentBinary is object); Debug.Assert(stack.Count > 0); var leftChild = (BoundExpression)Visit(stack.Peek().Left); do { currentBinary = stack.Pop(); bool foundInfo = _updatedNullabilities.TryGetValue(currentBinary, out (NullabilityInfo Info, TypeSymbol Type)infoAndType); var right = (BoundExpression)Visit(currentBinary.Right); var type = foundInfo ? infoAndType.Type : currentBinary.Type; currentBinary = currentBinary switch { BoundBinaryOperator binary => binary.Update(binary.OperatorKind, binary.ConstantValueOpt, GetUpdatedSymbol(binary, binary.MethodOpt), binary.ResultKind, binary.OriginalUserDefinedOperatorsOpt, leftChild, right, type), // https://github.com/dotnet/roslyn/issues/35031: We'll need to update logical.LogicalOperator BoundUserDefinedConditionalLogicalOperator logical => logical.Update(logical.OperatorKind, logical.LogicalOperator, logical.TrueOperator, logical.FalseOperator, logical.ResultKind, logical.OriginalUserDefinedOperatorsOpt, leftChild, right, type), _ => throw ExceptionUtilities.UnexpectedValue(currentBinary.Kind), }; if (foundInfo) { currentBinary.TopLevelNullability = infoAndType.Info; } leftChild = currentBinary; }while (stack.Count > 0); Debug.Assert(currentBinary != null); return(currentBinary !); }
public sealed override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundExpression child = node.Left; if (child.Kind != BoundKind.BinaryOperator) { return(base.VisitBinaryOperator(node)); } var stack = ArrayBuilder <BoundBinaryOperator> .GetInstance(); stack.Push(node); BoundBinaryOperator binary = (BoundBinaryOperator)child; while (true) { stack.Push(binary); child = binary.Left; if (child.Kind != BoundKind.BinaryOperator) { break; } binary = (BoundBinaryOperator)child; } var left = (BoundExpression)this.Visit(child); do { binary = stack.Pop(); var right = (BoundExpression)this.Visit(binary.Right); var type = this.VisitType(binary.Type); left = binary.Update(binary.OperatorKind, binary.ConstantValueOpt, binary.MethodOpt, binary.ResultKind, binary.OriginalUserDefinedOperatorsOpt, left, right, type); }while (stack.Count > 0); Debug.Assert((object)binary == node); stack.Free(); return(left); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { // check for common trivial cases like arr?.Length > 0 // in such situations we can fuse two operations and thus // avoid doing lifting. // In the given example if "arr" is null then the result is false, // otherwise we can do unlifted ">" directly on arr.Length if (node.OperatorKind.IsLifted() || node.OperatorKind == BinaryOperatorKind.NullableNullEqual || node.OperatorKind == BinaryOperatorKind.NullableNullNotEqual) { BoundExpression foldedWithConditionalAccess = TryFoldWithConditionalAccess(node); if (foldedWithConditionalAccess != null) { return foldedWithConditionalAccess; } } return VisitBinaryOperator(node, null); }
private static BoundExpression RewriteSwitchOperator( SyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray <BoundExpression> rewrittenLabels, ImmutableArray <BoundExpression> rewrittenValues, TypeSymbol rewrittenType, TypeSymbol booleanType) { Debug.Assert(rewrittenLabels.Length >= 1); Debug.Assert(rewrittenLabels.Length + 1 == rewrittenValues.Length); var label = rewrittenLabels[0]; var consequence = rewrittenValues[0]; var condition = new BoundBinaryOperator(label.Syntax, BinaryOperatorKind.Equal, rewrittenExpression, label, null, null, LookupResultKind.Viable, booleanType); BoundExpression alternative = rewrittenLabels.Length > 1 ? RewriteSwitchOperator(syntax, rewrittenExpression, rewrittenLabels.RemoveAt(0), rewrittenValues.RemoveAt(0), rewrittenType, booleanType) : rewrittenValues[1]; return(new BoundConditionalOperator(label.Syntax, condition, consequence, alternative, null, rewrittenType)); }
private void CheckBinaryOperator(BoundBinaryOperator node) { if (node.Method is MethodSymbol method) { if (_inExpressionLambda && method.IsAbstract && method.IsStatic) { Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node); } } else { CheckUnsafeType(node.Left); CheckUnsafeType(node.Right); } CheckForBitwiseOrSignExtend(node, node.OperatorKind, node.Left, node.Right); CheckNullableNullBinOp(node); CheckLiftedBinOp(node); CheckRelationals(node); CheckDynamic(node); }
private void CheckRelationals(BoundBinaryOperator node) { Debug.Assert(node != null); if (!node.OperatorKind.IsComparison()) { return; } // Don't bother to check vacuous comparisons where both sides are constant, eg, where someone // is doing something like "if (0xFFFFFFFFU == 0)" -- these are likely to be machine- // generated code. if (node.Left.ConstantValue != null && node.Right.ConstantValue == null && node.Right.Kind == BoundKind.Conversion) { CheckVacuousComparisons(node, node.Left.ConstantValue, node.Right); } if (node.Right.ConstantValue != null && node.Left.ConstantValue == null && node.Left.Kind == BoundKind.Conversion) { CheckVacuousComparisons(node, node.Right.ConstantValue, node.Left); } if (node.OperatorKind == BinaryOperatorKind.ObjectEqual || node.OperatorKind == BinaryOperatorKind.ObjectNotEqual) { TypeSymbol t; if (node.Left.Type.SpecialType == SpecialType.System_Object && !IsExplicitCast(node.Left) && !(node.Left.ConstantValue != null && node.Left.ConstantValue.IsNull) && ConvertedHasEqual(node.OperatorKind, node.Right, out t)) { // Possible unintended reference comparison; to get a value comparison, cast the left hand side to type '{0}' _diagnostics.Add(ErrorCode.WRN_BadRefCompareLeft, node.Syntax.Location, t); } else if (node.Right.Type.SpecialType == SpecialType.System_Object && !IsExplicitCast(node.Right) && !(node.Right.ConstantValue != null && node.Right.ConstantValue.IsNull) && ConvertedHasEqual(node.OperatorKind, node.Left, out t)) { // Possible unintended reference comparison; to get a value comparison, cast the right hand side to type '{0}' _diagnostics.Add(ErrorCode.WRN_BadRefCompareRight, node.Syntax.Location, t); } } CheckSelfComparisons(node); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { var result = base.VisitBinaryOperator(node); switch (node.OperatorKind) { case BinaryOperatorKind.ObjectEqual: case BinaryOperatorKind.ObjectNotEqual: var left = SkipReferenceConversions(node.Left); var right = SkipReferenceConversions(node.Right); if (left.IsLiteralNull()) { var tmp = left; left = right; right = tmp; } if (right.IsLiteralNull()) { Value v = MakeValue(left); if (v.creations.Any()) { Split(); foreach (var e in v.creations) { if (node.OperatorKind == BinaryOperatorKind.ObjectEqual) { this.StateWhenTrue.possiblyUndisposedCreations.Remove(e); } else { this.StateWhenFalse.possiblyUndisposedCreations.Remove(e); } } } } break; } return(result); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundSpillSequenceBuilder builder = null; var right = VisitExpression(ref builder, node.Right); BoundExpression left; if (builder == null) { left = VisitExpression(ref builder, node.Left); } else { var leftBuilder = new BoundSpillSequenceBuilder(); left = VisitExpression(ref leftBuilder, node.Left); left = Spill(leftBuilder, left); if (node.OperatorKind == BinaryOperatorKind.LogicalBoolOr || node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd) { var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); leftBuilder.AddLocal(tmp); leftBuilder.AddStatement(_F.Assignment(_F.Local(tmp), left)); leftBuilder.AddStatement(_F.If( node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd ? _F.Local(tmp) : _F.Not(_F.Local(tmp)), UpdateStatement(builder, _F.Assignment(_F.Local(tmp), right)))); return(UpdateExpression(leftBuilder, _F.Local(tmp))); } else { // if the right-hand-side has await, spill the left leftBuilder.Include(builder); builder = leftBuilder; } } return(UpdateExpression(builder, node.Update(node.OperatorKind, node.ConstantValue, node.MethodOpt, node.ResultKind, left, right, node.Type))); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundSpillSequence2 ss = null; var right = VisitExpression(ref ss, node.Right); BoundExpression left; if (ss == null) { left = VisitExpression(ref ss, node.Left); } else { var ssLeft = new BoundSpillSequence2(); left = VisitExpression(ref ssLeft, node.Left); left = Spill(ssLeft, left); if (node.OperatorKind == BinaryOperatorKind.LogicalBoolOr || node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd) { ssLeft.Add(F.If( node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd ? left : F.Not(left), UpdateStatement(ss, F.Assignment(left, right)) )); return UpdateExpression(ssLeft, left); } else { // if the right-hand-side has await, spill the left ssLeft.IncludeSequence(ss); ss = ssLeft; } } return UpdateExpression(ss, node.Update(node.OperatorKind, left, right, node.ConstantValue, node.MethodOpt, node.ResultKind, node.Type)); }
private BoundExpression MakeBinary(BoundBinaryOperator node, bool isLifted, bool requiresLifted, string opname, BoundExpression loweredLeft, BoundExpression loweredRight) { return ((object)node.MethodOpt == null) ? ExprFactory(opname, loweredLeft, loweredRight) : requiresLifted ? ExprFactory(opname, loweredLeft, loweredRight, Bound.Literal(isLifted && node.MethodOpt.ReturnType != node.Type), Bound.MethodInfo(node.MethodOpt)) : ExprFactory(opname, loweredLeft, loweredRight, Bound.MethodInfo(node.MethodOpt)); }
private void CheckOr(BoundBinaryOperator node) { // We wish to give a warning for situations where an unexpected sign extension wipes // out some bits. For example: // // int x = 0x0ABC0000; // short y = -2; // 0xFFFE // int z = x | y; // // The user might naively expect the result to be 0x0ABCFFFE. But the short is sign-extended // when it is converted to int before the bitwise or, so this is in fact the same as: // // int x = 0x0ABC0000; // short y = -2; // 0xFFFE // int ytemp = y; // 0xFFFFFFFE // int z = x | ytemp; // // Which gives 0xFFFFFFFE, not 0x0ABCFFFE. // // However, we wish to suppress the warning if: // // * The sign extension is "expected" -- for instance, because there was an explicit cast // from short to int: "int z = x | (int)y;" should not produce the warning. // Note that "uint z = (uint)x | (uint)y;" should still produce the warning because // the user might not realize that converting y to uint does a sign extension. // // * There is the same amount of sign extension on both sides. For example, when // doing "short | sbyte", both sides are sign extended. The left creates two FF bytes // and the right creates three, so we are potentially wiping out information from the // left side. But "short | short" adds two FF bytes on both sides, so no information is lost. // // The native compiler also suppresses this warning in a bizarre and inconsistent way. If // the side whose bits are going to be wiped out by sign extension is a *constant*, then the // warning is suppressed *if the constant, when converted to a signed long, fits into the // range of the type that is being sign-extended.* // // Consider the effects of this rule: // // (uint)0xFFFF0000 | y -- gives the warning because 0xFFFF0000 as a long is not in the range of a short, // *even though the result will not be affected by the sign extension*. // (ulong)0xFFFFFFFFFFFFFFFF | y -- suppresses the warning, because 0xFFFFFFFFFFFFFFFF as a signed long fits into a short. // (int)0x0000ABCD | y -- suppresses the warning, even though the 0000 is going to be wiped out by the sign extension. // // It seems clear that the intention of the heuristic is to *suppress the warning when the bits being hammered // on are either all zero, or all one.* Therefore that is the heuristic we will *actually* implement here. // switch (node.OperatorKind) { case BinaryOperatorKind.LiftedUIntOr: case BinaryOperatorKind.LiftedIntOr: case BinaryOperatorKind.LiftedULongOr: case BinaryOperatorKind.LiftedLongOr: case BinaryOperatorKind.UIntOr: case BinaryOperatorKind.IntOr: case BinaryOperatorKind.ULongOr: case BinaryOperatorKind.LongOr: break; default: return; } // The native compiler skips this warning if both sides of the operator are constants. // // CONSIDER: Is that sensible? It seems reasonable that if we would warn on int | short // when they are non-constants, or when one is a constant, that we would similarly warn // when both are constants. if (node.ConstantValue != null) { return; } // Start by determining *which bits on each side are going to be unexpectedly turned on*. ulong left = FindSurprisingSignExtensionBits(node.Left); ulong right = FindSurprisingSignExtensionBits(node.Right); // If they are all the same then there's no warning to give. if (left == right) { return; } // Suppress the warning if one side is a constant, and either all the unexpected // bits are already off, or all the unexpected bits are already on. ConstantValue constVal = GetConstantValueForBitwiseOrCheck(node.Left); if (constVal != null) { ulong val = constVal.UInt64Value; if ((val & right) == right || (~val & right) == right) { return; } } constVal = GetConstantValueForBitwiseOrCheck(node.Right); if (constVal != null) { ulong val = constVal.UInt64Value; if ((val & left) == left || (~val & left) == left) { return; } } // CS0675: Bitwise-or operator used on a sign-extended operand; consider casting to a smaller unsigned type first Error(ErrorCode.WRN_BitwiseOrSignExtend, node); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { BoundExpression left = (BoundExpression)this.Visit(node.Left); BoundExpression right = (BoundExpression)this.Visit(node.Right); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(left, right)) { return node.Update(node.OperatorKind, left, right, node.ConstantValueOpt, node.MethodOpt, node.ResultKind, type); } var subExprs = ReadOnlyArray<BoundExpression>.CreateFrom(left, right); var spillBuilder = new SpillBuilder(); var newArgs = SpillExpressionList(spillBuilder, subExprs); Debug.Assert(newArgs.Count == 2); var newBinaryOperator = node.Update( node.OperatorKind, newArgs[0], newArgs[1], node.ConstantValueOpt, node.MethodOpt, node.ResultKind, type); return spillBuilder.BuildSequenceAndFree(F, newBinaryOperator); }
/// <summary> /// Lower a foreach loop that will enumerate a multi-dimensional array. /// /// A[...] a = x; /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...; /// for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) /// for (int p_1 = a.GetLowerBound(1); p_1 <= q_1; p_1 = p_1 + 1) /// ... /// { V v = (V)a[p_0, p_1, ...]; /* body */ } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to nested for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(!arrayType.IsSZArray); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to // initializing each one at the corresponding level of nesting). Doing it at the same time as // the lower bound would make this code a bit simpler, but it would make it harder to compare // the roslyn and dev10 IL. // int q_0, q_1, ... LocalSymbol[] upperVar = new LocalSymbol[rank]; BoundLocal[] boundUpperVar = new BoundLocal[rank]; BoundStatement[] upperVarDecl = new BoundStatement[rank]; for (int dimension = 0; dimension < rank; dimension++) { // int q_dimension upperVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayLimit); boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType); ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetUpperBound(dimension) BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int q_dimension = a.GetUpperBound(dimension); upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound); } // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); // work from most-nested to least-nested // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(dimension) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // int p_dimension = a.GetLowerBound(dimension); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop ? node.BreakLabel // i.e. the one that break statements will jump to : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused // p_dimension <= q_dimension //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: boundUpperVar[dimension], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_dimension = p_dimension + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(positionVar[dimension]), rewrittenInitializer: positionVarDecl, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: body, breakLabel: breakLabel, continueLabel: continueLabel, hasErrors: node.HasErrors); } Debug.Assert(forLoop != null); BoundStatement result = new BoundBlock( forEachSyntax, ImmutableArray.Create(arrayVar).Concat(upperVar.AsImmutableOrNull()), ImmutableArray <LocalFunctionSymbol> .Empty, ImmutableArray.Create(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop)); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
/// <summary> /// Lower a foreach loop that will enumerate the characters of a string. /// /// string s = x; /// for (int p = 0; p < s.Length; p = p + 1) { /// V v = (V)s.Chars[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's /// implementation of IEnumerable and just indexing into its characters. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); TypeSymbol stringType = collectionExpression.Type; Debug.Assert(stringType.SpecialType == SpecialType.System_String); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // string s; LocalSymbol stringVar = _factory.SynthesizedLocal(stringType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); // int p; LocalSymbol positionVar = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); // Reference to s. BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // string s = /*expr*/; BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl); // int p = 0; BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar, MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType)); // string s = /*node.Expression*/; int p = 0; BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ImmutableArray.Create <BoundStatement>(stringVarDecl, positionVariableDecl)); MethodSymbol method = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length); BoundExpression stringLength = BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: method, arguments: ImmutableArray <BoundExpression> .Empty); // p < s.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: stringLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; Debug.Assert(node.ElementConversion.IsValid); // (V)s.Chars[p] MethodSymbol chars = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars); BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: chars, arguments: ImmutableArray.Create <BoundExpression>(boundPositionVar)), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)s.Chars[p]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)s.Chars[p]; /*node.Body*/ } BoundStatement loopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) { // V v = (V)s.Chars[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(stringVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove); MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics); if ((object)compareExchangeMethod == null) { return new BoundBlock(syntax, locals: ImmutableArray<LocalSymbol>.Empty, statements: ImmutableArray.Create<BoundStatement>( new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }; } GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create<BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create<BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; return new BoundBlock(syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create<BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }; }
private void CheckLiftedBinOp(BoundBinaryOperator node) { Debug.Assert(node != null); if (!node.OperatorKind.IsLifted()) { return; } switch (node.OperatorKind.Operator()) { case BinaryOperatorKind.LessThan: case BinaryOperatorKind.LessThanOrEqual: case BinaryOperatorKind.GreaterThan: case BinaryOperatorKind.GreaterThanOrEqual: // CS0464: Comparing with null of type '{0}' always produces 'false' // // Produce the warning if one (or both) sides are always null. if (node.Right.NullableNeverHasValue()) { Error(ErrorCode.WRN_CmpAlwaysFalse, node, GetTypeForLiftedComparisonWarning(node.Right)); } else if (node.Left.NullableNeverHasValue()) { Error(ErrorCode.WRN_CmpAlwaysFalse, node, GetTypeForLiftedComparisonWarning(node.Left)); } break; case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: // CS0472: The result of the expression is always '{0}' since a value of type '{1}' is never equal to 'null' of type '{2}' // // Produce the warning if one side is always null and the other is never null. // That is, we have something like "if (myInt == null)" string always = node.OperatorKind.Operator() == BinaryOperatorKind.NotEqual ? "true" : "false"; if (_compilation.FeatureStrictEnabled || !node.OperatorKind.IsUserDefined()) { if (node.Right.NullableNeverHasValue() && node.Left.NullableAlwaysHasValue()) { Error(node.OperatorKind.IsUserDefined() ? ErrorCode.WRN_NubExprIsConstBool2 : ErrorCode.WRN_NubExprIsConstBool, node, always, node.Left.Type.GetNullableUnderlyingType(), GetTypeForLiftedComparisonWarning(node.Right)); } else if (node.Left.NullableNeverHasValue() && node.Right.NullableAlwaysHasValue()) { Error(node.OperatorKind.IsUserDefined() ? ErrorCode.WRN_NubExprIsConstBool2 : ErrorCode.WRN_NubExprIsConstBool, node, always, node.Right.Type.GetNullableUnderlyingType(), GetTypeForLiftedComparisonWarning(node.Left)); } } break; case BinaryOperatorKind.Or: case BinaryOperatorKind.And: // CS0458: The result of the expression is always 'null' of type '{0}' if ((node.Left.NullableNeverHasValue() && node.Right.IsNullableNonBoolean()) || (node.Left.IsNullableNonBoolean() && node.Right.NullableNeverHasValue())) Error(ErrorCode.WRN_AlwaysNull, node, node.Type); break; default: // CS0458: The result of the expression is always 'null' of type '{0}' if (node.Right.NullableNeverHasValue() || node.Left.NullableNeverHasValue()) { Error(ErrorCode.WRN_AlwaysNull, node, node.Type); } break; } }
private void CheckNullableNullBinOp(BoundBinaryOperator node) { if ((node.OperatorKind & BinaryOperatorKind.NullableNull) == 0) { return; } switch (node.OperatorKind.Operator()) { case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: // CS0472: The result of the expression is always '{0}' since a value of type '{1}' is never equal to 'null' of type '{2}' // // Produce the warning if one side is always null and the other is never null. // That is, we have something like "if (myInt == null)" string always = node.OperatorKind.Operator() == BinaryOperatorKind.NotEqual ? "true" : "false"; // we use a separate warning code for cases newly detected in later versions of the compiler if (node.Right.IsLiteralNull() && node.Left.NullableAlwaysHasValue()) { Error(ErrorCode.WRN_NubExprIsConstBool, node, always, node.Left.Type.GetNullableUnderlyingType(), node.Left.Type); } else if (node.Left.IsLiteralNull() && node.Right.NullableAlwaysHasValue()) { Error(ErrorCode.WRN_NubExprIsConstBool, node, always, node.Right.Type.GetNullableUnderlyingType(), node.Right.Type); } break; } }
private void CheckVacuousComparisons(BoundBinaryOperator tree, ConstantValue constantValue, BoundNode operand) { Debug.Assert(tree != null); Debug.Assert(constantValue != null); Debug.Assert(operand != null); // We wish to detect comparisons between integers and constants which are likely to be wrong // because we know at compile time whether they will be true or false. For example: // // const short s = 1000; // byte b = whatever; // if (b < s) // // We know that this will always be true because when b and s are both converted to int for // the comparison, the left side will always be less than the right side. // // We only give the warning if there is no explicit conversion involved on the operand. // For example, if we had: // // const uint s = 1000; // sbyte b = whatever; // if ((byte)b < s) // // Then we do not give a warning. // // Note that the native compiler has what looks to be some dead code. It checks to see // if the conversion on the operand is from an enum type. But this is unnecessary if // we are rejecting cases with explicit conversions. The only possible cases are: // // enum == enumConstant -- enum types must be the same, so it must be in range. // enum == integralConstant -- not legal unless the constant is zero, which is in range. // enum == (ENUM)anyConstant -- if the constant is out of range then this is not legal in the first place // unless we're in an unchecked context, in which case, the user probably does // not want the warning. // integral == enumConstant -- never legal in the first place // // Since it seems pointless to try to check enums, we simply look for vacuous comparisons of // integral types here. for (BoundConversion conversion = operand as BoundConversion; conversion != null; conversion = conversion.Operand as BoundConversion) { if (conversion.ConversionKind != ConversionKind.ImplicitNumeric && conversion.ConversionKind != ConversionKind.ImplicitConstant) { return; } // As in dev11, we don't dig through explicit casts (see ExpressionBinder::WarnAboutBadRelationals). if (conversion.ExplicitCastInCode) { return; } if (!conversion.Operand.Type.SpecialType.IsIntegralType() || !conversion.Type.SpecialType.IsIntegralType()) { return; } if (!Binder.CheckConstantBounds(conversion.Operand.Type.SpecialType, constantValue)) { Error(ErrorCode.WRN_VacuousIntegralComp, tree, conversion.Operand.Type); return; } } }
private void CheckSelfComparisons(BoundBinaryOperator node) { Debug.Assert(node != null); Debug.Assert(node.OperatorKind.IsComparison()); if (!node.HasAnyErrors && IsSameLocalOrField(node.Left, node.Right)) { Error(ErrorCode.WRN_ComparisonToSelf, node); } }
private static void GetSymbolsAndResultKind(BoundBinaryOperator binaryOperator, out bool isDynamic, ref LookupResultKind resultKind, ref ImmutableArray<Symbol> symbols) { BinaryOperatorKind operandType = binaryOperator.OperatorKind.OperandTypes(); BinaryOperatorKind op = binaryOperator.OperatorKind.Operator(); isDynamic = binaryOperator.OperatorKind.IsDynamic(); if (operandType == 0 || operandType == BinaryOperatorKind.UserDefined || binaryOperator.ResultKind != LookupResultKind.Viable || binaryOperator.OperatorKind.IsLogical()) { if (!isDynamic) { GetSymbolsAndResultKind(binaryOperator, binaryOperator.MethodOpt, binaryOperator.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind); } } else { Debug.Assert((object)binaryOperator.MethodOpt == null && binaryOperator.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty); if (!isDynamic && (op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) && ((binaryOperator.Left.IsLiteralNull() && binaryOperator.Right.Type.IsNullableType()) || (binaryOperator.Right.IsLiteralNull() && binaryOperator.Left.Type.IsNullableType())) && binaryOperator.Type.SpecialType == SpecialType.System_Boolean) { // Comparison of a nullable type with null, return corresponding operator for Object. var objectType = binaryOperator.Type.ContainingAssembly.GetSpecialType(SpecialType.System_Object); symbols = ImmutableArray.Create<Symbol>(new SynthesizedIntrinsicOperatorSymbol(objectType, OperatorFacts.BinaryOperatorNameFromOperatorKind(op), objectType, binaryOperator.Type, binaryOperator.OperatorKind.IsChecked())); } else { symbols = ImmutableArray.Create(GetIntrinsicOperatorSymbol(op, isDynamic, binaryOperator.Left.Type, binaryOperator.Right.Type, binaryOperator.Type, binaryOperator.OperatorKind.IsChecked())); } resultKind = binaryOperator.ResultKind; } }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { throw ExceptionUtilities.Unreachable; }
/// <summary> /// Lower a foreach loop that will enumerate the characters of a string. /// /// string s = x; /// for (int p = 0; p < s.Length; p = p + 1) { /// V v = (V)s.Chars[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring string's /// implementation of IEnumerable and just indexing into its characters. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); TypeSymbol stringType = collectionExpression.Type; Debug.Assert(stringType.SpecialType == SpecialType.System_String); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // string s; LocalSymbol stringVar = _factory.SynthesizedLocal(stringType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); // int p; LocalSymbol positionVar = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); // Reference to s. BoundLocal boundStringVar = MakeBoundLocal(forEachSyntax, stringVar, stringType); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // string s = /*expr*/; BoundStatement stringVarDecl = MakeLocalDeclaration(forEachSyntax, stringVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref stringVarDecl); // int p = 0; BoundStatement positionVariableDecl = MakeLocalDeclaration(forEachSyntax, positionVar, MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType)); // string s = /*node.Expression*/; int p = 0; BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ImmutableArray.Create<BoundStatement>(stringVarDecl, positionVariableDecl)); MethodSymbol method = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length); BoundExpression stringLength = BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: method, arguments: ImmutableArray<BoundExpression>.Empty); // p < s.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: stringLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; Debug.Assert(node.ElementConversion.IsValid); // (V)s.Chars[p] MethodSymbol chars = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars); BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundStringVar, method: chars, arguments: ImmutableArray.Create<BoundExpression>(boundPositionVar)), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)s.Chars[p]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)s.Chars[p]; /*node.Body*/ } BoundStatement loopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); // for (string s = /*node.Expression*/, int p = 0; p < s.Length; p = p + 1) { // V v = (V)s.Chars[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(stringVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
/// <summary> /// Lower a foreach loop that will enumerate a single-dimensional array. /// /// A[] a = x; /// for (int p = 0; p < a.Length; p = p + 1) { /// V v = (V)a[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; Debug.Assert(arrayType.IsSZArray); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[] a LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); // A[] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p LocalSymbol positionVar = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // int p = 0; BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar, MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType)); // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess( syntax: forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create <BoundExpression>(boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p]; BoundStatement iterationVariableDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVariableDecl); BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ImmutableArray.Create <BoundStatement>(arrayVarDecl, positionVarDecl)); // a.Length BoundExpression arrayLength = new BoundArrayLength( syntax: forEachSyntax, expression: boundArrayVar, type: intType); // p < a.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: arrayLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); // { V v = (V)a[p]; /* node.Body */ } BoundStatement loopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVariableDecl, rewrittenBody, forEachSyntax); // for (A[] a = /*node.Expression*/, int p = 0; p < a.Length; p = p + 1) { // V v = (V)a[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: node.Syntax, outerLocals: ImmutableArray.Create <LocalSymbol>(arrayVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
/// <summary> /// Lower a foreach loop that will enumerate a single-dimensional array. /// /// A[] a = x; /// for (int p = 0; p < a.Length; p = p + 1) { /// V v = (V)a[p]; /// // body /// } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; Debug.Assert(arrayType.IsSZArray); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[] a LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); // A[] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p LocalSymbol positionVar = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); // Reference to p. BoundLocal boundPositionVar = MakeBoundLocal(forEachSyntax, positionVar, intType); // int p = 0; BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar, MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType)); // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess( syntax: forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create<BoundExpression>(boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p]; BoundStatement iterationVariableDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVariableDecl); BoundStatement initializer = new BoundStatementList(forEachSyntax, statements: ImmutableArray.Create<BoundStatement>(arrayVarDecl, positionVarDecl)); // a.Length BoundExpression arrayLength = new BoundArrayLength( syntax: forEachSyntax, expression: boundArrayVar, type: intType); // p < a.Length BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThan, left: boundPositionVar, right: arrayLength, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p = p + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType); // { V v = (V)a[p]; /* node.Body */ } BoundStatement loopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVariableDecl, rewrittenBody, forEachSyntax); // for (A[] a = /*node.Expression*/, int p = 0; p < a.Length; p = p + 1) { // V v = (V)a[p]; // /*node.Body*/ // } BoundStatement result = RewriteForStatement( syntax: node.Syntax, outerLocals: ImmutableArray.Create<LocalSymbol>(arrayVar, positionVar), rewrittenInitializer: initializer, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: loopBody, breakLabel: node.BreakLabel, continueLabel: node.ContinueLabel, hasErrors: node.HasErrors); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
private void CheckBinaryOperator(BoundBinaryOperator node) { if ((object)node.MethodOpt == null) { CheckUnsafeType(node.Left); CheckUnsafeType(node.Right); } CheckForBitwiseOrSignExtend(node, node.OperatorKind, node.Left, node.Right); CheckNullableNullBinOp(node); CheckLiftedBinOp(node); CheckRelationals(node); CheckDynamic(node); }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// /// Note, if System.Threading.Interlocked.CompareExchange<T> is not available, /// we emit the following code and mark the method Synchronized (unless it is a struct). /// /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -= /// /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); SpecialMember updateMethodId = isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove; MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(updateMethodId); BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; if (updateMethod == null) { MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId); diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, memberDescriptor.DeclaringTypeMetadataName, memberDescriptor.Name), syntax.Location)); return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>(@return)) { WasCompilerGenerated = true }); } Binder.ReportUseSiteDiagnostics(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; BoundExpression delegateUpdate; MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Threading_Interlocked__CompareExchange_T); if ((object)compareExchangeMethod == null) { // (DelegateType)Delegate.Combine(_event, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // _event = (DelegateType)Delegate.Combine(_event, value); BoundStatement eventUpdate = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundBackingField, right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>( eventUpdate, @return)) { WasCompilerGenerated = true }); } compareExchangeMethod = compareExchangeMethod.Construct(ImmutableArray.Create <TypeSymbol>(delegateType)); Binder.ReportUseSiteDiagnostics(compareExchangeMethod, diagnostics, syntax); GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
private BoundExpression VisitBinaryOperator(BoundBinaryOperator node) { var opKind = node.OperatorKind; var op = opKind & BinaryOperatorKind.OpMask; var isChecked = (opKind & BinaryOperatorKind.Checked) != 0; var isLogical = (opKind & BinaryOperatorKind.Logical) != 0; var isLifted = (opKind & BinaryOperatorKind.Lifted) != 0; bool requiresLifted = false; string opname; switch (op) { case BinaryOperatorKind.Addition: opname = isChecked ? "AddChecked" : "Add"; break; case BinaryOperatorKind.Multiplication: opname = isChecked ? "MultiplyChecked" : "Multiply"; break; case BinaryOperatorKind.Subtraction: opname = isChecked ? "SubtractChecked" : "Subtract"; break; case BinaryOperatorKind.Division: opname = "Divide"; break; case BinaryOperatorKind.Remainder: opname = "Modulo"; break; case BinaryOperatorKind.And: opname = isLogical ? "AndAlso" : "And"; break; case BinaryOperatorKind.Xor: opname = "ExclusiveOr"; break; case BinaryOperatorKind.Or: opname = isLogical ? "OrElse" : "Or"; break; case BinaryOperatorKind.LeftShift: opname = "LeftShift"; break; case BinaryOperatorKind.RightShift: opname = "RightShift"; break; case BinaryOperatorKind.Equal: opname = "Equal"; requiresLifted = true; break; case BinaryOperatorKind.NotEqual: opname = "NotEqual"; requiresLifted = true; break; case BinaryOperatorKind.LessThan: opname = "LessThan"; requiresLifted = true; break; case BinaryOperatorKind.LessThanOrEqual: opname = "LessThanOrEqual"; requiresLifted = true; break; case BinaryOperatorKind.GreaterThan: opname = "GreaterThan"; requiresLifted = true; break; case BinaryOperatorKind.GreaterThanOrEqual: opname = "GreaterThanOrEqual"; requiresLifted = true; break; default: throw ExceptionUtilities.UnexpectedValue(op); } BoundExpression left = node.Left; BoundExpression right = node.Right; // Fix up the null value for a nullable comparison vs null if ((object)left.Type == null && left.IsLiteralNull()) left = Bound.Default(right.Type); if ((object)right.Type == null && right.IsLiteralNull()) right = Bound.Default(left.Type); var loweredLeft = Visit(left); var loweredRight = Visit(right); // Enums are handled as per their promoted underlying type switch (node.OperatorKind.OperandTypes()) { case BinaryOperatorKind.Enum: case BinaryOperatorKind.EnumAndUnderlying: case BinaryOperatorKind.UnderlyingAndEnum: { var enumOperand = (node.OperatorKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left; var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType()); if (isLifted) promotedType = NullableType.Construct(promotedType); loweredLeft = Convert(loweredLeft, left.Type, promotedType, isChecked, false); loweredRight = Convert(loweredRight, right.Type, promotedType, isChecked, false); var result = MakeBinary(node, isLifted, requiresLifted, opname, loweredLeft, loweredRight); return Demote(result, node.Type, isChecked); } default: return MakeBinary(node, isLifted, requiresLifted, opname, loweredLeft, loweredRight); } }
private BoundExpression MakeBinaryOperator( BoundBinaryOperator oldNode, CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method, bool isPointerElementAccess = false, bool isCompoundAssignment = false, BoundUnaryOperator applyParentUnaryOperator = null) { Debug.Assert(oldNode == null || (oldNode.Syntax == syntax)); if (_inExpressionLambda) { switch (operatorKind.Operator() | operatorKind.OperandTypes()) { case BinaryOperatorKind.ObjectAndStringConcatenation: case BinaryOperatorKind.StringAndObjectConcatenation: case BinaryOperatorKind.StringConcatenation: return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.DelegateCombination: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Combine); case BinaryOperatorKind.DelegateRemoval: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Remove); case BinaryOperatorKind.DelegateEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Equality); case BinaryOperatorKind.DelegateNotEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Inequality); } } else // try to lower the expression. { if (operatorKind.IsDynamic()) { Debug.Assert(!isPointerElementAccess); if (operatorKind.IsLogical()) { return MakeDynamicLogicalBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, method, type, isCompoundAssignment, applyParentUnaryOperator); } else { Debug.Assert((object)method == null); return _dynamicFactory.MakeDynamicBinaryOperator(operatorKind, loweredLeft, loweredRight, isCompoundAssignment, type).ToExpression(); } } if (operatorKind.IsLifted()) { return RewriteLiftedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); } if (operatorKind.IsUserDefined()) { return LowerUserDefinedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method); } switch (operatorKind.OperatorWithLogical() | operatorKind.OperandTypes()) { case BinaryOperatorKind.NullableNullEqual: case BinaryOperatorKind.NullableNullNotEqual: return RewriteNullableNullEquality(syntax, operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.ObjectAndStringConcatenation: case BinaryOperatorKind.StringAndObjectConcatenation: case BinaryOperatorKind.StringConcatenation: return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.StringEqual: return RewriteStringEquality(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_String__op_Equality); case BinaryOperatorKind.StringNotEqual: return RewriteStringEquality(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_String__op_Inequality); case BinaryOperatorKind.DelegateCombination: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Combine); case BinaryOperatorKind.DelegateRemoval: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Remove); case BinaryOperatorKind.DelegateEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Equality); case BinaryOperatorKind.DelegateNotEqual: return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Inequality); case BinaryOperatorKind.LogicalBoolAnd: if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight; if (loweredLeft.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredRight.Kind == BoundKind.Local || loweredRight.Kind == BoundKind.Parameter) { operatorKind &= ~BinaryOperatorKind.Logical; } goto default; case BinaryOperatorKind.LogicalBoolOr: if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; if (loweredLeft.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredRight.Kind == BoundKind.Local || loweredRight.Kind == BoundKind.Parameter) { operatorKind &= ~BinaryOperatorKind.Logical; } goto default; case BinaryOperatorKind.BoolAnd: if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight; goto default; case BinaryOperatorKind.BoolOr: if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; goto default; case BinaryOperatorKind.BoolEqual: if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight; if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.False) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type); if (loweredRight.ConstantValue == ConstantValue.False) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type); goto default; case BinaryOperatorKind.BoolNotEqual: if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type); if (loweredRight.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type); goto default; case BinaryOperatorKind.BoolXor: if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight; if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft; if (loweredLeft.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type); if (loweredRight.ConstantValue == ConstantValue.True) return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type); goto default; case BinaryOperatorKind.IntLeftShift: case BinaryOperatorKind.UIntLeftShift: case BinaryOperatorKind.IntRightShift: case BinaryOperatorKind.UIntRightShift: return RewriteBuiltInShiftOperation(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, 0x1F); case BinaryOperatorKind.LongLeftShift: case BinaryOperatorKind.ULongLeftShift: case BinaryOperatorKind.LongRightShift: case BinaryOperatorKind.ULongRightShift: return RewriteBuiltInShiftOperation(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, 0x3F); case BinaryOperatorKind.DecimalAddition: case BinaryOperatorKind.DecimalSubtraction: case BinaryOperatorKind.DecimalMultiplication: case BinaryOperatorKind.DecimalDivision: case BinaryOperatorKind.DecimalRemainder: case BinaryOperatorKind.DecimalEqual: case BinaryOperatorKind.DecimalNotEqual: case BinaryOperatorKind.DecimalLessThan: case BinaryOperatorKind.DecimalLessThanOrEqual: case BinaryOperatorKind.DecimalGreaterThan: case BinaryOperatorKind.DecimalGreaterThanOrEqual: return RewriteDecimalBinaryOperation(syntax, loweredLeft, loweredRight, operatorKind); case BinaryOperatorKind.PointerAndIntAddition: case BinaryOperatorKind.PointerAndUIntAddition: case BinaryOperatorKind.PointerAndLongAddition: case BinaryOperatorKind.PointerAndULongAddition: case BinaryOperatorKind.PointerAndIntSubtraction: case BinaryOperatorKind.PointerAndUIntSubtraction: case BinaryOperatorKind.PointerAndLongSubtraction: case BinaryOperatorKind.PointerAndULongSubtraction: if (loweredRight.IsDefaultValue()) { return loweredLeft; } return RewritePointerNumericOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isPointerElementAccess, isLeftPointer: true); case BinaryOperatorKind.IntAndPointerAddition: case BinaryOperatorKind.UIntAndPointerAddition: case BinaryOperatorKind.LongAndPointerAddition: case BinaryOperatorKind.ULongAndPointerAddition: if (loweredLeft.IsDefaultValue()) { return loweredRight; } return RewritePointerNumericOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isPointerElementAccess, isLeftPointer: false); case BinaryOperatorKind.PointerSubtraction: return RewritePointerSubtraction(operatorKind, loweredLeft, loweredRight, type); case BinaryOperatorKind.IntAddition: case BinaryOperatorKind.UIntAddition: case BinaryOperatorKind.LongAddition: case BinaryOperatorKind.ULongAddition: if (loweredLeft.IsDefaultValue()) { return loweredRight; } if (loweredRight.IsDefaultValue()) { return loweredLeft; } goto default; case BinaryOperatorKind.IntSubtraction: case BinaryOperatorKind.LongSubtraction: case BinaryOperatorKind.UIntSubtraction: case BinaryOperatorKind.ULongSubtraction: if (loweredRight.IsDefaultValue()) { return loweredLeft; } goto default; case BinaryOperatorKind.IntMultiplication: case BinaryOperatorKind.LongMultiplication: case BinaryOperatorKind.UIntMultiplication: case BinaryOperatorKind.ULongMultiplication: if (loweredLeft.IsDefaultValue()) { return loweredLeft; } if (loweredRight.IsDefaultValue()) { return loweredRight; } if (loweredLeft.ConstantValue?.UInt64Value == 1) { return loweredRight; } if (loweredRight.ConstantValue?.UInt64Value == 1) { return loweredLeft; } goto default; case BinaryOperatorKind.IntGreaterThan: case BinaryOperatorKind.IntLessThanOrEqual: if (loweredLeft.Kind == BoundKind.ArrayLength && loweredRight.IsDefaultValue()) { //array length is never negative var newOp = operatorKind == BinaryOperatorKind.IntGreaterThan ? BinaryOperatorKind.NotEqual : BinaryOperatorKind.Equal; operatorKind &= ~BinaryOperatorKind.OpMask; operatorKind |= newOp; loweredLeft = UnconvertArrayLength((BoundArrayLength)loweredLeft); } goto default; case BinaryOperatorKind.IntLessThan: case BinaryOperatorKind.IntGreaterThanOrEqual: if (loweredRight.Kind == BoundKind.ArrayLength && loweredLeft.IsDefaultValue()) { //array length is never negative var newOp = operatorKind == BinaryOperatorKind.IntLessThan ? BinaryOperatorKind.NotEqual : BinaryOperatorKind.Equal; operatorKind &= ~BinaryOperatorKind.OpMask; operatorKind |= newOp; loweredRight = UnconvertArrayLength((BoundArrayLength)loweredRight); } goto default; case BinaryOperatorKind.IntEqual: case BinaryOperatorKind.IntNotEqual: if (loweredLeft.Kind == BoundKind.ArrayLength && loweredRight.IsDefaultValue()) { loweredLeft = UnconvertArrayLength((BoundArrayLength)loweredLeft); } else if (loweredRight.Kind == BoundKind.ArrayLength && loweredLeft.IsDefaultValue()) { loweredRight = UnconvertArrayLength((BoundArrayLength)loweredRight); } goto default; default: break; } } return (oldNode != null) ? oldNode.Update(operatorKind, loweredLeft, loweredRight, oldNode.ConstantValueOpt, oldNode.MethodOpt, oldNode.ResultKind, type) : new BoundBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, null, null, LookupResultKind.Viable, type); }
private BoundExpression RewriteStringEquality(BoundBinaryOperator oldNode, CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, SpecialMember member) { if (oldNode != null && (loweredLeft.ConstantValue == ConstantValue.Null || loweredRight.ConstantValue == ConstantValue.Null)) { return oldNode.Update(operatorKind, loweredLeft, loweredRight, oldNode.ConstantValueOpt, oldNode.MethodOpt, oldNode.ResultKind, type); } var method = GetSpecialTypeMethod(syntax, member); Debug.Assert((object)method != null); return BoundCall.Synthesized(syntax, null, method, loweredLeft, loweredRight); }
private BoundExpression VisitBinaryOperator(BoundBinaryOperator node) { var opKind = node.OperatorKind; var op = opKind & BinaryOperatorKind.OpMask; var isChecked = (opKind & BinaryOperatorKind.Checked) != 0; var isLogical = (opKind & BinaryOperatorKind.Logical) != 0; var isLifted = (opKind & BinaryOperatorKind.Lifted) != 0; bool requiresLifted = false; string opname; switch (op) { case BinaryOperatorKind.Addition: opname = isChecked ? "AddChecked" : "Add"; break; case BinaryOperatorKind.Multiplication: opname = isChecked ? "MultiplyChecked" : "Multiply"; break; case BinaryOperatorKind.Subtraction: opname = isChecked ? "SubtractChecked" : "Subtract"; break; case BinaryOperatorKind.Division: opname = "Divide"; break; case BinaryOperatorKind.Remainder: opname = "Modulo"; break; case BinaryOperatorKind.And: opname = isLogical ? "AndAlso" : "And"; break; case BinaryOperatorKind.Xor: opname = "ExclusiveOr"; break; case BinaryOperatorKind.Or: opname = isLogical ? "OrElse" : "Or"; break; case BinaryOperatorKind.LeftShift: opname = "LeftShift"; break; case BinaryOperatorKind.RightShift: opname = "RightShift"; break; case BinaryOperatorKind.Equal: opname = "Equal"; requiresLifted = true; break; case BinaryOperatorKind.NotEqual: opname = "NotEqual"; requiresLifted = true; break; case BinaryOperatorKind.LessThan: opname = "LessThan"; requiresLifted = true; break; case BinaryOperatorKind.LessThanOrEqual: opname = "LessThanOrEqual"; requiresLifted = true; break; case BinaryOperatorKind.GreaterThan: opname = "GreaterThan"; requiresLifted = true; break; case BinaryOperatorKind.GreaterThanOrEqual: opname = "GreaterThanOrEqual"; requiresLifted = true; break; default: throw ExceptionUtilities.UnexpectedValue(op); } BoundExpression left = node.Left; BoundExpression right = node.Right; // Fix up the null value for a nullable comparison vs null if ((object)left.Type == null && left.IsLiteralNull()) { left = Bound.Default(right.Type); } if ((object)right.Type == null && right.IsLiteralNull()) { right = Bound.Default(left.Type); } var loweredLeft = Visit(left); var loweredRight = Visit(right); // Enums are handled as per their promoted underlying type switch (node.OperatorKind.OperandTypes()) { case BinaryOperatorKind.Enum: case BinaryOperatorKind.EnumAndUnderlying: case BinaryOperatorKind.UnderlyingAndEnum: { var enumOperand = (node.OperatorKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left; var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType()); if (isLifted) { promotedType = NullableType.Construct(promotedType); } loweredLeft = Convert(loweredLeft, left.Type, promotedType, isChecked, false); loweredRight = Convert(loweredRight, right.Type, promotedType, isChecked, false); var result = MakeBinary(node, isLifted, requiresLifted, opname, loweredLeft, loweredRight); return(Demote(result, node.Type, isChecked)); } default: return(MakeBinary(node, isLifted, requiresLifted, opname, loweredLeft, loweredRight)); } }
/// <summary> /// Lower a foreach loop that will enumerate a multi-dimensional array. /// /// A[...] a = x; /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...; /// for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) /// for (int p_1 = a.GetLowerBound(1); p_1 <= q_1; p_1 = p_1 + 1) /// ... /// { V v = (V)a[p_0, p_1, ...]; /* body */ } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to nested for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(!arrayType.IsSZArray); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to // initializing each one at the corresponding level of nesting). Doing it at the same time as // the lower bound would make this code a bit simpler, but it would make it harder to compare // the roslyn and dev10 IL. // int q_0, q_1, ... LocalSymbol[] upperVar = new LocalSymbol[rank]; BoundLocal[] boundUpperVar = new BoundLocal[rank]; BoundStatement[] upperVarDecl = new BoundStatement[rank]; for (int dimension = 0; dimension < rank; dimension++) { // int q_dimension upperVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayLimit); boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType); ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetUpperBound(dimension) BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int q_dimension = a.GetUpperBound(dimension); upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound); } // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); // work from most-nested to least-nested // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(dimension) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // int p_dimension = a.GetLowerBound(dimension); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop ? node.BreakLabel // i.e. the one that break statements will jump to : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused // p_dimension <= q_dimension //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: boundUpperVar[dimension], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_dimension = p_dimension + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(positionVar[dimension]), rewrittenInitializer: positionVarDecl, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: body, breakLabel: breakLabel, continueLabel: continueLabel, hasErrors: node.HasErrors); } Debug.Assert(forLoop != null); BoundStatement result = new BoundBlock( forEachSyntax, ImmutableArray.Create(arrayVar).Concat(upperVar.AsImmutableOrNull()), ImmutableArray<LocalFunctionSymbol>.Empty, ImmutableArray.Create(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop)); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return result; }
private void CheckBinaryOperator(BoundBinaryOperator node) { if ((object)node.MethodOpt == null) { CheckUnsafeType(node.Left); CheckUnsafeType(node.Right); } CheckOr(node); CheckNullableNullBinOp(node); CheckLiftedBinOp(node); CheckRelationals(node); CheckDynamic(node); }
private void CheckLiftedBinOp(BoundBinaryOperator node) { Debug.Assert(node != null); if (!node.OperatorKind.IsLifted()) { return; } switch (node.OperatorKind.Operator()) { case BinaryOperatorKind.LessThan: case BinaryOperatorKind.LessThanOrEqual: case BinaryOperatorKind.GreaterThan: case BinaryOperatorKind.GreaterThanOrEqual: // CS0464: Comparing with null of type '{0}' always produces 'false' // // Produce the warning if one (or both) sides are always null. if (node.Right.NullableNeverHasValue()) { Error(ErrorCode.WRN_CmpAlwaysFalse, node, GetTypeForLiftedComparisonWarning(node.Right)); } else if (node.Left.NullableNeverHasValue()) { Error(ErrorCode.WRN_CmpAlwaysFalse, node, GetTypeForLiftedComparisonWarning(node.Left)); } break; case BinaryOperatorKind.Equal: case BinaryOperatorKind.NotEqual: // CS0472: The result of the expression is always '{0}' since a value of type '{1}' is never equal to 'null' of type '{2}' // // Produce the warning if one side is always null and the other is never null. // That is, we have something like "if (myInt == null)" string always = node.OperatorKind.Operator() == BinaryOperatorKind.NotEqual ? "true" : "false"; if (_compilation.FeatureStrictEnabled || !node.OperatorKind.IsUserDefined()) { if (node.Right.NullableNeverHasValue() && node.Left.NullableAlwaysHasValue()) { Error(node.OperatorKind.IsUserDefined() ? ErrorCode.WRN_NubExprIsConstBool2 : ErrorCode.WRN_NubExprIsConstBool, node, always, node.Left.Type.GetNullableUnderlyingType(), GetTypeForLiftedComparisonWarning(node.Right)); } else if (node.Left.NullableNeverHasValue() && node.Right.NullableAlwaysHasValue()) { Error(node.OperatorKind.IsUserDefined() ? ErrorCode.WRN_NubExprIsConstBool2 : ErrorCode.WRN_NubExprIsConstBool, node, always, node.Right.Type.GetNullableUnderlyingType(), GetTypeForLiftedComparisonWarning(node.Left)); } } break; case BinaryOperatorKind.Or: case BinaryOperatorKind.And: // CS0458: The result of the expression is always 'null' of type '{0}' if ((node.Left.NullableNeverHasValue() && node.Right.IsNullableNonBoolean()) || (node.Left.IsNullableNonBoolean() && node.Right.NullableNeverHasValue())) { Error(ErrorCode.WRN_AlwaysNull, node, node.Type); } break; default: // CS0458: The result of the expression is always 'null' of type '{0}' if (node.Right.NullableNeverHasValue() || node.Left.NullableNeverHasValue()) { Error(ErrorCode.WRN_AlwaysNull, node, node.Type); } break; } }
private BoundExpression MakeCall( BoundCall node, SyntaxNode syntax, BoundExpression rewrittenReceiver, MethodSymbol method, ImmutableArray<BoundExpression> rewrittenArguments, ImmutableArray<RefKind> argumentRefKinds, bool invokedAsExtensionMethod, LookupResultKind resultKind, TypeSymbol type, ImmutableArray<LocalSymbol> temps = default(ImmutableArray<LocalSymbol>)) { BoundExpression rewrittenBoundCall; if (method.IsStatic && method.ContainingType.IsObjectType() && !_inExpressionLambda && (object)method == (object)_compilation.GetSpecialTypeMember(SpecialMember.System_Object__ReferenceEquals)) { Debug.Assert(rewrittenArguments.Length == 2); // ECMA - 335 // I.8.2.5.1 Identity // ... // Identity is implemented on System.Object via the ReferenceEquals method. rewrittenBoundCall = new BoundBinaryOperator( syntax, BinaryOperatorKind.ObjectEqual, rewrittenArguments[0], rewrittenArguments[1], null, null, resultKind, type); } else if (node == null) { rewrittenBoundCall = new BoundCall( syntax, rewrittenReceiver, method, rewrittenArguments, default(ImmutableArray<string>), argumentRefKinds, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: invokedAsExtensionMethod, argsToParamsOpt: default(ImmutableArray<int>), resultKind: resultKind, type: type); } else { rewrittenBoundCall = node.Update( rewrittenReceiver, method, rewrittenArguments, default(ImmutableArray<string>), argumentRefKinds, node.IsDelegateCall, false, node.InvokedAsExtensionMethod, default(ImmutableArray<int>), node.ResultKind, node.Type); } if (!temps.IsDefaultOrEmpty) { return new BoundSequence( syntax, locals: temps, sideEffects: ImmutableArray<BoundExpression>.Empty, value: rewrittenBoundCall, type: type); } return rewrittenBoundCall; }
public override BoundNode?VisitBinaryOperator(BoundBinaryOperator node) { return(VisitBinaryOperatorBase(node)); }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { // It is very common for bound trees to be left-heavy binary operators, eg, // a + b + c + d + ... // To avoid blowing the stack, do not recurse down the left hand side. // In order to avoid blowing the stack, we end up visiting right children // before left children; this should not be a problem in the diagnostics // pass. BoundBinaryOperator current = node; while (true) { CheckBinaryOperator(current); Visit(current.Right); if (current.Left.Kind == BoundKind.BinaryOperator) { current = (BoundBinaryOperator)current.Left; } else { Visit(current.Left); break; } } return null; }
public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) { return VisitBinaryOperator(node, null); }
private void CheckDynamic(BoundBinaryOperator node) { if (_inExpressionLambda && node.OperatorKind.IsDynamic()) { Error(ErrorCode.ERR_ExpressionTreeContainsDynamicOperation, node); } }
/// <summary> /// Spec section 7.9: if the left operand is int or uint, mask the right operand with 0x1F; /// if the left operand is long or ulong, mask the right operand with 0x3F. /// </summary> private BoundExpression RewriteBuiltInShiftOperation( BoundBinaryOperator oldNode, CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, int rightMask) { CSharpSyntaxNode rightSyntax = loweredRight.Syntax; ConstantValue rightConstantValue = loweredRight.ConstantValue; TypeSymbol rightType = loweredRight.Type; Debug.Assert(rightType.SpecialType == SpecialType.System_Int32); if (rightConstantValue != null && rightConstantValue.IsIntegral) { int shiftAmount = rightConstantValue.Int32Value & rightMask; if (shiftAmount == 0) { return loweredLeft; } loweredRight = MakeLiteral(rightSyntax, ConstantValue.Create(shiftAmount), rightType); } else { BinaryOperatorKind andOperatorKind = (operatorKind & ~BinaryOperatorKind.OpMask) | BinaryOperatorKind.And; loweredRight = new BoundBinaryOperator( rightSyntax, andOperatorKind, loweredRight, MakeLiteral(rightSyntax, ConstantValue.Create(rightMask), rightType), null, null, LookupResultKind.Viable, rightType); } return oldNode == null ? new BoundBinaryOperator( syntax, operatorKind, loweredLeft, loweredRight, null, null, LookupResultKind.Viable, type) : oldNode.Update( operatorKind, loweredLeft, loweredRight, null, null, oldNode.ResultKind, type); }
private BoundExpression MakeCall( BoundCall node, CSharpSyntaxNode syntax, BoundExpression rewrittenReceiver, MethodSymbol method, ImmutableArray <BoundExpression> rewrittenArguments, ImmutableArray <RefKind> argumentRefKinds, bool invokedAsExtensionMethod, LookupResultKind resultKind, TypeSymbol type, ImmutableArray <LocalSymbol> temps = default(ImmutableArray <LocalSymbol>)) { BoundExpression rewrittenBoundCall; if (method.IsStatic && method.ContainingType.IsObjectType() && !inExpressionLambda && (object)method == (object)this.compilation.GetSpecialTypeMember(SpecialMember.System_Object__ReferenceEquals)) { Debug.Assert(rewrittenArguments.Length == 2); // ECMA - 335 // I.8.2.5.1 Identity // ... // Identity is implemented on System.Object via the ReferenceEquals method. rewrittenBoundCall = new BoundBinaryOperator( syntax, BinaryOperatorKind.ObjectEqual, rewrittenArguments[0], rewrittenArguments[1], null, null, resultKind, type); } else if (node == null) { rewrittenBoundCall = new BoundCall( syntax, rewrittenReceiver, method, rewrittenArguments, default(ImmutableArray <string>), argumentRefKinds, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: invokedAsExtensionMethod, argsToParamsOpt: default(ImmutableArray <int>), resultKind: resultKind, type: type); } else { rewrittenBoundCall = node.Update( rewrittenReceiver, method, rewrittenArguments, default(ImmutableArray <string>), argumentRefKinds, node.IsDelegateCall, false, node.InvokedAsExtensionMethod, default(ImmutableArray <int>), node.ResultKind, node.Type); } if (!temps.IsDefaultOrEmpty) { return(new BoundSequence( syntax, locals: temps, sideEffects: ImmutableArray <BoundExpression> .Empty, value: rewrittenBoundCall, type: type)); } return(rewrittenBoundCall); }