private void UnaryOperatorEasyOut(UnaryOperatorKind kind, BoundExpression operand, UnaryOperatorOverloadResolutionResult result)
        {
            var operandType = operand.Type;

            if ((object)operandType == null)
            {
                return;
            }

            var easyOut = UnopEasyOut.OpKind(kind, operandType);

            if (easyOut == UnaryOperatorKind.Error)
            {
                return;
            }

            UnaryOperatorSignature signature = this.Compilation.builtInOperators.GetSignature(easyOut);

            Conversion?conversion = Conversions.FastClassifyConversion(operandType, signature.OperandType);

            Debug.Assert(conversion.HasValue && conversion.Value.IsImplicit);

            result.Results.Add(UnaryOperatorAnalysisResult.Applicable(signature, conversion.Value));
        }
        private static UnaryOperatorSignature?GetPointerOperation(UnaryOperatorKind kind, BoundExpression operand)
        {
            Debug.Assert(operand != null);

            var pointerType = operand.Type as PointerTypeSymbol;

            if ((object)pointerType == null)
            {
                return(null);
            }

            UnaryOperatorSignature?op = null;

            switch (kind)
            {
            case UnaryOperatorKind.PostfixIncrement:
            case UnaryOperatorKind.PostfixDecrement:
            case UnaryOperatorKind.PrefixIncrement:
            case UnaryOperatorKind.PrefixDecrement:
                op = new UnaryOperatorSignature(kind | UnaryOperatorKind.Pointer, pointerType, pointerType);
                break;
            }
            return(op);
        }
 public static UnaryOperatorAnalysisResult Inapplicable(UnaryOperatorSignature signature, Conversion conversion)
 {
     return(new UnaryOperatorAnalysisResult(OperatorAnalysisResultKind.Inapplicable, signature, conversion));
 }
 private UnaryOperatorAnalysisResult(OperatorAnalysisResultKind kind, UnaryOperatorSignature signature, Conversion conversion)
 {
     this.Kind       = kind;
     this.Signature  = signature;
     this.Conversion = conversion;
 }
Exemple #5
0
        /// <summary>
        /// If an element-wise binary operator returns a non-bool type, we will either:
        /// - prepare a conversion to bool if one exists
        /// - prepare a truth operator: op_false in the case of an equality (<c>a == b</c> will be lowered to <c>!((a == b).op_false)</c>) or op_true in the case of inequality,
        ///     with the conversion being used for its input.
        /// </summary>
        private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpressionSyntax node, BinaryOperatorKind binaryOperator, DiagnosticBag diagnostics,
                                                           out Conversion conversionForBool, out UnaryOperatorSignature boolOperator)
        {
            // Is the operand implicitly convertible to bool?

            HashSet <DiagnosticInfo> useSiteDiagnostics = null;
            TypeSymbol boolean    = GetSpecialType(SpecialType.System_Boolean, diagnostics, node);
            Conversion conversion = this.Conversions.ClassifyImplicitConversionFromType(type, boolean, ref useSiteDiagnostics);

            diagnostics.Add(node, useSiteDiagnostics);

            if (conversion.IsImplicit)
            {
                ReportDiagnosticsIfObsolete(diagnostics, conversion, node, hasBaseReceiver: false);
                conversionForBool = conversion;
                boolOperator      = default;
                return;
            }

            // It was not. Does it implement operator true (or false)?

            UnaryOperatorKind boolOpKind;

            switch (binaryOperator)
            {
            case BinaryOperatorKind.Equal:
                boolOpKind = UnaryOperatorKind.False;
                break;

            case BinaryOperatorKind.NotEqual:
                boolOpKind = UnaryOperatorKind.True;
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(binaryOperator);
            }

            LookupResultKind resultKind;
            ImmutableArray <MethodSymbol> originalUserDefinedOperators;
            BoundExpression             comparisonResult = new BoundTupleOperandPlaceholder(node, type);
            UnaryOperatorAnalysisResult best             = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, out resultKind, out originalUserDefinedOperators);

            if (best.HasValue)
            {
                conversionForBool = best.Conversion;
                boolOperator      = best.Signature;
                return;
            }

            // It did not. Give a "not convertible to bool" error.

            GenerateImplicitConversionError(diagnostics, node, conversion, comparisonResult, boolean);
            conversionForBool = Conversion.NoConversion;
            boolOperator      = default;
            return;
        }
Exemple #6
0
        private BetterResult BetterOperator(
            UnaryOperatorSignature op1,
            UnaryOperatorSignature op2,
            BoundExpression operand,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo
            )
        {
            // First we see if the conversion from the operand to one operand type is better than
            // the conversion to the other.

            BetterResult better = BetterConversionFromExpression(
                operand,
                op1.OperandType,
                op2.OperandType,
                ref useSiteInfo
                );

            if (better == BetterResult.Left || better == BetterResult.Right)
            {
                return(better);
            }

            // There was no better member on the basis of conversions. Go to the tiebreaking round.

            // SPEC: In case the parameter type sequences P1, P2 and Q1, Q2 are equivalent -- that is, every Pi
            // SPEC: has an identity conversion to the corresponding Qi -- the following tie-breaking rules
            // SPEC: are applied:

            if (Conversions.HasIdentityConversion(op1.OperandType, op2.OperandType))
            {
                // SPEC: If Mp has more specific parameter types than Mq then Mp is better than Mq.

                // Under what circumstances can two unary operators with identical signatures be "more specific"
                // than another? With a binary operator you could have C<T>.op+(C<T>, T) and C<T>.op+(C<T>, int).
                // When doing overload resolution on C<int> + int, the latter is more specific. But with a unary
                // operator, the sole operand *must* be the containing type or its nullable type. Therefore
                // if there is an identity conversion, then the parameters really were identical. We therefore
                // skip checking for specificity.

                // SPEC: If one member is a non-lifted operator and the other is a lifted operator,
                // SPEC: the non-lifted one is better.

                bool lifted1 = op1.Kind.IsLifted();
                bool lifted2 = op2.Kind.IsLifted();

                if (lifted1 && !lifted2)
                {
                    return(BetterResult.Right);
                }
                else if (!lifted1 && lifted2)
                {
                    return(BetterResult.Left);
                }
            }

            // Always prefer operators with val parameters over operators with in parameters:
            if (op1.RefKind == RefKind.None && op2.RefKind == RefKind.In)
            {
                return(BetterResult.Left);
            }
            else if (op2.RefKind == RefKind.None && op1.RefKind == RefKind.In)
            {
                return(BetterResult.Right);
            }

            return(BetterResult.Neither);
        }
        private static UnaryOperatorSignature? GetPointerOperation(UnaryOperatorKind kind, BoundExpression operand)
        {
            Debug.Assert(operand != null);

            var pointerType = operand.Type as PointerTypeSymbol;
            if ((object)pointerType == null)
            {
                return null;
            }

            UnaryOperatorSignature? op = null;
            switch (kind)
            {
                case UnaryOperatorKind.PostfixIncrement:
                case UnaryOperatorKind.PostfixDecrement:
                case UnaryOperatorKind.PrefixIncrement:
                case UnaryOperatorKind.PrefixDecrement:
                    op = new UnaryOperatorSignature(kind | UnaryOperatorKind.Pointer, pointerType, pointerType);
                    break;
            }
            return op;
        }
        private BetterResult BetterOperator(UnaryOperatorSignature op1, UnaryOperatorSignature op2, BoundExpression operand, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // First we see if the conversion from the operand to one operand type is better than 
            // the conversion to the other.

            BetterResult better = BetterConversionFromExpression(operand, op1.OperandType, op2.OperandType, ref useSiteDiagnostics);

            if (better == BetterResult.Left || better == BetterResult.Right)
            {
                return better;
            }

            // There was no better member on the basis of conversions. Go to the tiebreaking round.

            // SPEC: In case the parameter type sequences P1, P2 and Q1, Q2 are equivalent -- that is, every Pi
            // SPEC: has an identity conversion to the corresponding Qi -- the following tie-breaking rules
            // SPEC: are applied:

            if (Conversions.HasIdentityConversion(op1.OperandType, op2.OperandType))
            {
                // SPEC: If Mp has more specific parameter types than Mq then Mp is better than Mq.

                // Under what circumstances can two unary operators with identical signatures be "more specific"
                // than another? With a binary operator you could have C<T>.op+(C<T>, T) and C<T>.op+(C<T>, int).
                // When doing overload resolution on C<int> + int, the latter is more specific. But with a unary
                // operator, the sole operand *must* be the containing type or its nullable type. Therefore
                // if there is an identity conversion, then the parameters really were identical. We therefore
                // skip checking for specificity.

                // SPEC: If one member is a non-lifted operator and the other is a lifted operator,
                // SPEC: the non-lifted one is better.

                bool lifted1 = op1.Kind.IsLifted();
                bool lifted2 = op2.Kind.IsLifted();

                if (lifted1 && !lifted2)
                {
                    return BetterResult.Right;
                }
                else if (!lifted1 && lifted2)
                {
                    return BetterResult.Left;
                }
            }

            return BetterResult.Neither;
        }
        private void PrepareBoolConversionAndTruthOperator(TypeSymbol type, BinaryExpressionSyntax node, BinaryOperatorKind binaryOperator, BindingDiagnosticBag diagnostics,
                                                           out BoundExpression conversionForBool, out BoundValuePlaceholder conversionForBoolPlaceholder, out UnaryOperatorSignature boolOperator)
        {
            // Is the operand implicitly convertible to bool?

            CompoundUseSiteInfo <AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
            TypeSymbol boolean    = GetSpecialType(SpecialType.System_Boolean, diagnostics, node);
            Conversion conversion = this.Conversions.ClassifyImplicitConversionFromType(type, boolean, ref useSiteInfo);

            diagnostics.Add(node, useSiteInfo);

            if (conversion.IsImplicit)
            {
                conversionForBoolPlaceholder = new BoundValuePlaceholder(node, type).MakeCompilerGenerated();
                conversionForBool            = CreateConversion(node, conversionForBoolPlaceholder, conversion, isCast: false, conversionGroupOpt: null, boolean, diagnostics);
                boolOperator = default;
                return;
            }

            // It was not. Does it implement operator true (or false)?

            UnaryOperatorKind boolOpKind;

            switch (binaryOperator)
            {
            case BinaryOperatorKind.Equal:
                boolOpKind = UnaryOperatorKind.False;
                break;

            case BinaryOperatorKind.NotEqual:
                boolOpKind = UnaryOperatorKind.True;
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(binaryOperator);
            }

            LookupResultKind resultKind;
            ImmutableArray <MethodSymbol> originalUserDefinedOperators;
            BoundExpression             comparisonResult = new BoundTupleOperandPlaceholder(node, type);
            UnaryOperatorAnalysisResult best             = this.UnaryOperatorOverloadResolution(boolOpKind, comparisonResult, node, diagnostics, out resultKind, out originalUserDefinedOperators);

            if (best.HasValue)
            {
                conversionForBoolPlaceholder = new BoundValuePlaceholder(node, type).MakeCompilerGenerated();
                conversionForBool            = CreateConversion(node, conversionForBoolPlaceholder, best.Conversion, isCast: false, conversionGroupOpt: null, best.Signature.OperandType, diagnostics);
                boolOperator = best.Signature;
                return;
            }

            // It did not. Give a "not convertible to bool" error.

            GenerateImplicitConversionError(diagnostics, node, conversion, comparisonResult, boolean);
            conversionForBoolPlaceholder = null;
            conversionForBool            = null;
            boolOperator = default;
            return;
        }
Exemple #10
0
        /// <summary>
        /// Produce an element-wise comparison and logic to ensure the result is a bool type.
        ///
        /// If an element-wise comparison doesn't return bool, then:
        /// - if it is dynamic, we'll do `!(comparisonResult.false)` or `comparisonResult.true`
        /// - if it implicitly converts to bool, we'll just do the conversion
        /// - otherwise, we'll do `!(comparisonResult.false)` or `comparisonResult.true` (as we'd do for `if` or `while`)
        /// </summary>
        private BoundExpression RewriteTupleSingleOperator(TupleBinaryOperatorInfo.Single single,
                                                           BoundExpression left, BoundExpression right, TypeSymbol boolType, BinaryOperatorKind operatorKind)
        {
            if (single.Kind.IsDynamic())
            {
                // Produce
                // !((left == right).op_false)
                // (left != right).op_true

                BoundExpression dynamicResult = _dynamicFactory.MakeDynamicBinaryOperator(single.Kind, left, right, isCompoundAssignment: false, _compilation.DynamicType).ToExpression();
                if (operatorKind == BinaryOperatorKind.Equal)
                {
                    return(_factory.Not(MakeUnaryOperator(UnaryOperatorKind.DynamicFalse, left.Syntax, method: null, dynamicResult, boolType)));
                }
                else
                {
                    return(MakeUnaryOperator(UnaryOperatorKind.DynamicTrue, left.Syntax, method: null, dynamicResult, boolType));
                }
            }

            if (left.IsLiteralNull() && right.IsLiteralNull())
            {
                // For `null == null` this is special-cased during initial binding
                return(new BoundLiteral(left.Syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.Equal), boolType));
            }

            // We leave both operands in nullable-null conversions unconverted, MakeBinaryOperator has special for null-literal
            bool            isNullableNullConversion = single.Kind.OperandTypes() == BinaryOperatorKind.NullableNull;
            BoundExpression convertedLeft            = isNullableNullConversion
                ? left
                : MakeConversionNode(left.Syntax, left, single.LeftConversion, single.LeftConvertedTypeOpt, @checked: false);

            BoundExpression convertedRight = isNullableNullConversion
                ? right
                : MakeConversionNode(right.Syntax, right, single.RightConversion, single.RightConvertedTypeOpt, @checked: false);

            BoundExpression        binary         = MakeBinaryOperator(_factory.Syntax, single.Kind, convertedLeft, convertedRight, single.MethodSymbolOpt?.ReturnType ?? boolType, single.MethodSymbolOpt);
            UnaryOperatorSignature boolOperator   = single.BoolOperator;
            Conversion             boolConversion = single.ConversionForBool;

            BoundExpression result;

            if (boolOperator.Kind != UnaryOperatorKind.Error)
            {
                // Produce
                // !((left == right).op_false)
                // (left != right).op_true
                BoundExpression convertedBinary = MakeConversionNode(_factory.Syntax, binary, boolConversion, boolOperator.OperandType, @checked: false);

                Debug.Assert(boolOperator.ReturnType.SpecialType == SpecialType.System_Boolean);
                result = MakeUnaryOperator(boolOperator.Kind, binary.Syntax, boolOperator.Method, convertedBinary, boolType);

                if (operatorKind == BinaryOperatorKind.Equal)
                {
                    result = _factory.Not(result);
                }
            }
            else if (!boolConversion.IsIdentity)
            {
                // Produce
                // (bool)(left == right)
                // (bool)(left != right)
                result = MakeConversionNode(_factory.Syntax, binary, boolConversion, boolType, @checked: false);
            }
            else
            {
                result = binary;
            }

            return(result);
        }
 public static UnaryOperatorAnalysisResult Inapplicable(UnaryOperatorSignature signature, Conversion conversion)
 {
     return new UnaryOperatorAnalysisResult(OperatorAnalysisResultKind.Inapplicable, signature, conversion);
 }
 private UnaryOperatorAnalysisResult(OperatorAnalysisResultKind kind, UnaryOperatorSignature signature, Conversion conversion)
 {
     this.Kind = kind;
     this.Signature = signature;
     this.Conversion = conversion;
 }