Ejemplo n.º 1
0
 private void CheckLiftedUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
 {
     // 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);
     }
 }
Ejemplo n.º 2
0
        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,
                        BoundBinaryOperator.UncommonData.CreateIfNeeded(binary.ConstantValue, GetUpdatedSymbol(binary, binary.Method), binary.ConstrainedToType, binary.OriginalUserDefinedOperatorsOpt),
                        binary.ResultKind,
                        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.ConstrainedToTypeOpt, 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 !);
        }
Ejemplo n.º 3
0
        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;

#pragma warning disable IDE0055 // Fix formatting
                // https://github.com/dotnet/roslyn/issues/35031: We'll need to update the symbols for the internal methods/operators used in the binary operators
                currentBinary = currentBinary switch
                {
                    BoundBinaryOperator binary => (BoundBinaryOperatorBase)binary.Update(binary.OperatorKind, binary.ConstantValueOpt, binary.MethodOpt, binary.ResultKind, leftChild, right, type),
                    BoundUserDefinedConditionalLogicalOperator logical => logical.Update(logical.OperatorKind, logical.LogicalOperator, logical.TrueOperator, logical.FalseOperator, logical.ResultKind, leftChild, right, type),
                    _ => throw ExceptionUtilities.UnexpectedValue(currentBinary.Kind),
                };
#pragma warning restore IDE0055 // Fix formatting

                if (foundInfo)
                {
                    currentBinary.TopLevelNullability = infoAndType.Info;
                }

                leftChild = currentBinary;
            }while (stack.Count > 0);

            Debug.Assert(currentBinary != null);
            return(currentBinary);
        }
        public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
        {
            // Yes, we could have a lifted, logical, user-defined operator:
            //
            // struct C { 
            //   public static C operator &(C x, C y) {...}
            //   public static bool operator true(C? c) { ... }
            //   public static bool operator false(C? c) { ... }
            // }
            //
            // If we have C? q, r and we say q && r then this gets bound as 
            // C? tempQ = q ;
            // C.false(tempQ) ? 
            //     tempQ : 
            //     ( 
            //         C? tempR = r ; 
            //         tempQ.HasValue & tempR.HasValue ? 
            //           new C?(C.&(tempQ.GetValueOrDefault(), tempR.GetValueOrDefault())) :
            //           default C?()
            //     )
            //
            // Note that the native compiler does not allow q && r. However, the native compiler
            // *does* allow q && r if C is defined as:
            //
            // struct C { 
            //   public static C? operator &(C? x, C? y) {...}
            //   public static bool operator true(C? c) { ... }
            //   public static bool operator false(C? c) { ... }
            // }
            //
            // It seems unusual and wrong that an & operator should be allowed to become
            // a && operator if there is a "manually lifted" operator in source, but not
            // if there is a "synthesized" lifted operator.  Roslyn fixes this bug.
            //
            // Anyway, in this case we must lower this to its non-logical form, and then
            // lower the interior of that to its non-lifted form.

            // See comments in method IsValidUserDefinedConditionalLogicalOperator for information
            // on some subtle aspects of this lowering.

            // We generate one of:
            //
            // x || y --> temp = x; T.true(temp)  ? temp : T.|(temp, y);
            // x && y --> temp = x; T.false(temp) ? temp : T.&(temp, y);
            //
            // For the ease of naming locals, we'll assume we're doing an &&.

            // TODO: We generate every one of these as "temp = x; T.false(temp) ? temp : T.&(temp, y)" even
            // TODO: when x has no side effects. We can optimize away the temporary if there are no side effects.

            var syntax = node.Syntax;
            var operatorKind = node.OperatorKind;
            var type = node.Type;

            BoundExpression loweredLeft = VisitExpression(node.Left);
            BoundExpression loweredRight = VisitExpression(node.Right);

            if (_inExpressionLambda)
            {
                return node.Update(operatorKind, loweredLeft, loweredRight, node.LogicalOperator, node.TrueOperator, node.FalseOperator, node.ResultKind, type);
            }

            BoundAssignmentOperator tempAssignment;
            var boundTemp = _factory.StoreToTemp(loweredLeft, out tempAssignment);

            // T.false(temp)
            var falseOperatorCall = BoundCall.Synthesized(syntax, null, operatorKind.Operator() == BinaryOperatorKind.And ? node.FalseOperator : node.TrueOperator, boundTemp);

            // T.&(temp, y)
            var andOperatorCall = LowerUserDefinedBinaryOperator(syntax, operatorKind & ~BinaryOperatorKind.Logical, boundTemp, loweredRight, type, node.LogicalOperator);

            // T.false(temp) ? temp : T.&(temp, y)
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: falseOperatorCall,
                rewrittenConsequence: boundTemp,
                rewrittenAlternative: andOperatorCall,
                constantValueOpt: null,
                rewrittenType: type);

            // temp = x; T.false(temp) ? temp : T.&(temp, y)
            return new BoundSequence(
                syntax: syntax,
                locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignment),
                value: conditionalExpression,
                type: type);
        }
Ejemplo n.º 5
0
 public override BoundNode?VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
 {
     return(VisitBinaryOperatorBase(node));
 }
Ejemplo n.º 6
0
 public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
 {
     throw ExceptionUtilities.Unreachable;
 }
Ejemplo n.º 7
0
 public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
 {
     CheckLiftedUserDefinedConditionalLogicalOperator(node);
     return(base.VisitUserDefinedConditionalLogicalOperator(node));
 }
 public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
 {
     CheckLiftedUserDefinedConditionalLogicalOperator(node);
     return base.VisitUserDefinedConditionalLogicalOperator(node);
 }
 private void CheckLiftedUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node)
 {
     // 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);
     }
 }