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 )); }
private BoundExpression BindDynamicBinaryOperator( BinaryExpressionSyntax node, BinaryOperatorKind kind, BoundExpression left, BoundExpression right, DiagnosticBag diagnostics) { // This method binds binary * / % + - << >> < > <= >= == != & ! ^ && || 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; bool leftValidOperand = IsLegalDynamicOperand(left); bool rightValidOperand = IsLegalDynamicOperand(right); if (!leftValidOperand || !rightValidOperand) { // 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; } MethodSymbol userDefinedOperator = null; if (kind.IsLogical() && leftValidOperand) { // We need to make sure left is either implicitly convertible to Boolean or has user defined truth operator. // left && right is lowered to {op_False|op_Implicit}(left) ? left : And(left, right) // left || right is lowered to {op_True|!op_Implicit}(left) ? left : Or(left, right) HashSet<DiagnosticInfo> useSiteDiagnostics = null; if (!IsValidDynamicCondition(left, isNegative: kind == BinaryOperatorKind.LogicalAnd, useSiteDiagnostics: ref useSiteDiagnostics, userDefinedOperator: out userDefinedOperator)) { // Dev11 reports ERR_MustHaveOpTF. The error was shared between this case and user-defined binary Boolean operators. // We report two distinct more specific error messages. Error(diagnostics, ErrorCode.ERR_InvalidDynamicCondition, node.Left, left.Type, kind == BinaryOperatorKind.LogicalAnd ? "false" : "true"); hasError = true; } diagnostics.Add(node, useSiteDiagnostics); } return new BoundBinaryOperator( syntax: node, operatorKind: (hasError ? kind : kind.WithType(BinaryOperatorKind.Dynamic)).WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), left: left, right: right, constantValueOpt: ConstantValue.NotAvailable, methodOpt: userDefinedOperator, resultKind: LookupResultKind.Viable, type: Compilation.DynamicType, hasErrors: hasError); }