private BoundExpression ApplyConvertedTypes(BoundExpression expr, TupleBinaryOperatorInfo @operator, bool isRight, DiagnosticBag diagnostics)
        {
            TypeSymbol convertedType = isRight ? @operator.RightConvertedTypeOpt : @operator.LeftConvertedTypeOpt;

            if (convertedType is null)
            {
                if (@operator.InfoKind == TupleBinaryOperatorInfoKind.Multiple && expr.Kind == BoundKind.TupleLiteral)
                {
                    // Although the tuple will remain typeless, we'll give elements converted types as possible
                    var multiple = (TupleBinaryOperatorInfo.Multiple)@operator;
                    if (multiple.Operators.Length == 0)
                    {
                        return(expr);
                    }

                    var tuple = (BoundTupleLiteral)expr;
                    ImmutableArray <BoundExpression> arguments = tuple.Arguments;
                    int length = arguments.Length;
                    Debug.Assert(length == multiple.Operators.Length);

                    var builder = ArrayBuilder <BoundExpression> .GetInstance(length);

                    for (int i = 0; i < length; i++)
                    {
                        builder.Add(ApplyConvertedTypes(arguments[i], multiple.Operators[i], isRight, diagnostics));
                    }

                    return(tuple.Update(argumentNamesOpt: default, inferredNamesOpt: default, builder.ToImmutableAndFree(), tuple.Type));
Esempio n. 2
0
        private TupleBinaryOperatorInfo BindTupleDynamicBinaryOperatorSingleInfo(
            BinaryExpressionSyntax node,
            BinaryOperatorKind kind,
            BoundExpression left,
            BoundExpression right,
            BindingDiagnosticBag diagnostics
            )
        {
            // This method binds binary == and != operators where one or both of the operands are dynamic.
            Debug.Assert(
                (object)left.Type != null && left.Type.IsDynamic() ||
                (object)right.Type != null && right.Type.IsDynamic()
                );

            bool hasError = false;

            if (!IsLegalDynamicOperand(left) || !IsLegalDynamicOperand(right))
            {
                // Operator '{0}' cannot be applied to operands of type '{1}' and '{2}'
                Error(
                    diagnostics,
                    ErrorCode.ERR_BadBinaryOps,
                    node,
                    node.OperatorToken.Text,
                    left.Display,
                    right.Display
                    );
                hasError = true;
            }

            BinaryOperatorKind elementOperatorKind = hasError
                ? kind
                : kind.WithType(BinaryOperatorKind.Dynamic);
            TypeSymbol dynamicType = hasError ? CreateErrorType() : Compilation.DynamicType;

            // We'll want to dynamically invoke operators op_true (/op_false) for equality (/inequality) comparison, but we don't need
            // to prepare either a conversion or a truth operator. Those can just be synthesized during lowering.
            return(new TupleBinaryOperatorInfo.Single(
                       dynamicType,
                       dynamicType,
                       elementOperatorKind,
                       methodSymbolOpt: null,
                       conversionForBool: Conversion.Identity,
                       boolOperator: default
                       ));
        }
Esempio n. 3
0
        private BoundExpression RewriteTupleOperator(TupleBinaryOperatorInfo @operator,
                                                     BoundExpression left, BoundExpression right, TypeSymbol boolType,
                                                     ArrayBuilder <LocalSymbol> temps, BinaryOperatorKind operatorKind)
        {
            switch (@operator.InfoKind)
            {
            case TupleBinaryOperatorInfoKind.Multiple:
                return(RewriteTupleNestedOperators((TupleBinaryOperatorInfo.Multiple)@operator, left, right, boolType, temps, operatorKind));

            case TupleBinaryOperatorInfoKind.Single:
                return(RewriteTupleSingleOperator((TupleBinaryOperatorInfo.Single)@operator, left, right, boolType, operatorKind));

            case TupleBinaryOperatorInfoKind.NullNull:
                var nullnull = (TupleBinaryOperatorInfo.NullNull)@operator;
                return(new BoundLiteral(left.Syntax, ConstantValue.Create(nullnull.Kind == BinaryOperatorKind.Equal), boolType));

            default:
                throw ExceptionUtilities.UnexpectedValue(@operator.InfoKind);
            }
        }
Esempio n. 4
0
        private BoundExpression ApplyConvertedTypes(BoundExpression expr, TupleBinaryOperatorInfo @operator, bool isRight, DiagnosticBag diagnostics)
        {
            TypeSymbol convertedType = isRight ? @operator.RightConvertedTypeOpt : @operator.LeftConvertedTypeOpt;

            if (convertedType is null)
            {
                // Note: issues with default will already have been reported by BindSimpleBinaryOperator (ie. we couldn't find a suitable element-wise operator)
                if (@operator.InfoKind == TupleBinaryOperatorInfoKind.Multiple && expr is BoundTupleLiteral tuple)
                {
                    // Although the tuple will remain typeless, we'll give elements converted types as possible
                    var multiple = (TupleBinaryOperatorInfo.Multiple)@operator;
                    if (multiple.Operators.Length == 0)
                    {
                        return(BindToNaturalType(expr, diagnostics, reportNoTargetType: false));
                    }

                    ImmutableArray <BoundExpression> arguments = tuple.Arguments;
                    int length = arguments.Length;
                    Debug.Assert(length == multiple.Operators.Length);

                    var builder = ArrayBuilder <BoundExpression> .GetInstance(length);

                    for (int i = 0; i < length; i++)
                    {
                        builder.Add(ApplyConvertedTypes(arguments[i], multiple.Operators[i], isRight, diagnostics));
                    }

                    return(new BoundConvertedTupleLiteral(
                               tuple.Syntax, tuple, wasTargetTyped: false, builder.ToImmutableAndFree(), tuple.ArgumentNamesOpt, tuple.InferredNamesOpt, tuple.Type, tuple.HasErrors));
                }

                // This element isn't getting a converted type
                return(BindToNaturalType(expr, diagnostics, reportNoTargetType: false));
            }

            // We were able to determine a converted type (for this tuple literal or element), we can just convert to it
            return(GenerateConversionForAssignment(convertedType, expr, diagnostics));
        }
Esempio n. 5
0
        /// <summary>
        /// Walk down tuple literals and replace all the side-effecting elements that need saving with temps.
        /// Expressions that are not tuple literals need saving, and tuple literals that are involved in a simple comparison rather than a tuple comparison.
        /// </summary>
        private BoundExpression ReplaceTerminalElementsWithTemps(BoundExpression expr, TupleBinaryOperatorInfo operators, ArrayBuilder <BoundExpression> initEffects, ArrayBuilder <LocalSymbol> temps)
        {
            if (operators.InfoKind == TupleBinaryOperatorInfoKind.Multiple)
            {
                // Example:
                // in `(expr1, expr2) == (..., ...)` we need to save `expr1` and `expr2`
                if (expr.Kind == BoundKind.TupleLiteral)
                {
                    var tuple    = (BoundTupleLiteral)expr;
                    var multiple = (TupleBinaryOperatorInfo.Multiple)operators;
                    var builder  = ArrayBuilder <BoundExpression> .GetInstance(tuple.Arguments.Length);

                    for (int i = 0; i < tuple.Arguments.Length; i++)
                    {
                        var argument    = tuple.Arguments[i];
                        var newArgument = ReplaceTerminalElementsWithTemps(argument, multiple.Operators[i], initEffects, temps);
                        builder.Add(newArgument);
                    }
                    return(new BoundTupleLiteral(tuple.Syntax, tuple.ArgumentNamesOpt, tuple.InferredNamesOpt, builder.ToImmutableAndFree(), tuple.Type, tuple.HasErrors));
                }
            }

            // Examples:
            // in `expr == (..., ...)` we need to save `expr` because it's not a tuple literal
            // in `(..., expr) == (..., (..., ...))` we need to save `expr` because it is used in a simple comparison
            return(EvaluateSideEffectingArgumentToTemp(VisitExpression(expr), initEffects, ref temps));
        }
        /// <summary>
        /// Walk down tuple literals and replace all the side-effecting elements that need saving with temps.
        /// Expressions that are not tuple literals need saving, and tuple literals that are involved in a simple comparison rather than a tuple comparison.
        /// </summary>
        private BoundExpression ReplaceTerminalElementsWithTemps(BoundExpression expr, TupleBinaryOperatorInfo operators, ArrayBuilder <BoundExpression> initEffects, ArrayBuilder <LocalSymbol> temps)
        {
            if (operators.InfoKind == TupleBinaryOperatorInfoKind.Multiple)
            {
                // Example:
                // in `(expr1, expr2) == (..., ...)` we need to save `expr1` and `expr2`
                if (expr.Kind == BoundKind.TupleLiteral)
                {
                    var tuple    = (BoundTupleLiteral)expr;
                    var multiple = (TupleBinaryOperatorInfo.Multiple)operators;
                    var builder  = ArrayBuilder <BoundExpression> .GetInstance(tuple.Arguments.Length);

                    for (int i = 0; i < tuple.Arguments.Length; i++)
                    {
                        var argument    = tuple.Arguments[i];
                        var newArgument = ReplaceTerminalElementsWithTemps(argument, multiple.Operators[i], initEffects, temps);
                        builder.Add(newArgument);
                    }
                    return(new BoundTupleLiteral(tuple.Syntax, tuple.ArgumentNamesOpt, tuple.InferredNamesOpt, builder.ToImmutableAndFree(), tuple.Type, tuple.HasErrors));
                }
            }

            // Examples:
            // in `expr == (..., ...)` we need to save `expr` because it's not a tuple literal
            // in `(..., expr) == (..., (..., ...))` we need to save `expr` because it is used in a simple comparison
            BoundExpression loweredExpr = VisitExpression(expr);

            if ((object)loweredExpr.Type != null)
            {
                BoundExpression value = NullableAlwaysHasValue(loweredExpr);
                if (value != null)
                {
                    // Optimization: if the nullable expression always has a value, we'll replace that value
                    // with a temp saving that value
                    BoundExpression savedValue     = EvaluateSideEffectingArgumentToTemp(value, initEffects, temps);
                    var             objectCreation = (BoundObjectCreationExpression)loweredExpr;
                    return(objectCreation.UpdateArgumentsAndInitializer(ImmutableArray.Create(savedValue), objectCreation.ArgumentRefKindsOpt, objectCreation.InitializerExpressionOpt));
                }
            }

            // Note: lowered nullable expressions that never have a value also don't have side-effects
            return(EvaluateSideEffectingArgumentToTemp(loweredExpr, initEffects, temps));
        }