/// <summary> /// Returns the type to be used as a field type; generates errors in case the type is not /// supported for anonymous type fields. /// </summary> private TypeSymbol GetAnonymousTypeFieldType(BoundExpression expression, CSharpSyntaxNode errorSyntax, DiagnosticBag diagnostics, ref bool hasError) { object errorArg = null; TypeSymbol expressionType = expression.Type; if (!expression.HasAnyErrors) { if (expression.HasExpressionType()) { if (expressionType.SpecialType == SpecialType.System_Void) { errorArg = expressionType; expressionType = CreateErrorType(SyntaxFacts.GetText(SyntaxKind.VoidKeyword)); } else if (expressionType.IsUnsafe()) { errorArg = expressionType; // CONSIDER: we could use an explicit error type instead of the unsafe type. } else if (expressionType.IsRestrictedType()) { errorArg = expressionType; } } else { if (expression.Kind == BoundKind.UnboundLambda) { errorArg = ((UnboundLambda)expression).MessageID.Localize(); } else if (expression.Kind == BoundKind.MethodGroup) { errorArg = MessageID.IDS_MethodGroup.Localize(); } else { Debug.Assert(expression.IsLiteralNull(), "How did we successfully bind an expression without a type?"); errorArg = MessageID.IDS_NULL.Localize(); } } } if ((object)expressionType == null) { expressionType = CreateErrorType("error"); } if (errorArg != null) { hasError = true; Error(diagnostics, ErrorCode.ERR_AnonymousTypePropertyAssignedBadValue, errorSyntax, errorArg); // NOTE: ERR_QueryRangeVariableAssignedBadValue is being generated // by query binding code and never reach this point } return(expressionType); }
protected override Conversion ClassifyNullConversionFromExpression(BoundExpression sourceExpression, TypeSymbol source, TypeSymbol destination, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { if (sourceExpression.Kind == BoundKind.Literal && sourceExpression.IsLiteralNull()) { Conversion result; if (ClassifyVoNullLiteralConversion(sourceExpression, destination, out result) != ConversionKind.NoConversion) { return(result); } } return(Conversion.NoConversion); }
private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSymbol methodOpt, TypeSymbol type, BoundExpression left, BoundExpression right) { bool isChecked, isLifted, requiresLifted; string opName = GetBinaryOperatorName(opKind, out isChecked, out isLifted, out requiresLifted); // 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); } // Enums are handled as per their promoted underlying type switch (opKind.OperandTypes()) { case BinaryOperatorKind.EnumAndUnderlying: case BinaryOperatorKind.UnderlyingAndEnum: case BinaryOperatorKind.Enum: { var enumOperand = (opKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left; var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType()); if (opKind.IsLifted()) { promotedType = _nullableType.Construct(promotedType); } var loweredLeft = VisitAndPromoteEnumOperand(left, promotedType, isChecked); var loweredRight = VisitAndPromoteEnumOperand(right, promotedType, isChecked); var result = MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight); return(Demote(result, type, isChecked)); } default: { var loweredLeft = Visit(left); var loweredRight = Visit(right); return(MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight)); } } }
private static ConversionKind ClassifyNullLiteralConversion(BoundExpression source, TypeSymbol destination) { Debug.Assert((object)source != null); Debug.Assert((object)destination != null); if (!source.IsLiteralNull()) { return(ConversionKind.NoConversion); } // SPEC: An implicit conversion exists from the null literal to any nullable type. if (destination.IsNullableType()) { // The spec defines a "null literal conversion" specifically as a conversion from // null to nullable type. return(ConversionKind.NullLiteral); } // SPEC: An implicit conversion exists from the null literal to any reference type. // SPEC: An implicit conversion exists from the null literal to type parameter T, // SPEC: provided T is known to be a reference type. [...] The conversion [is] classified // SPEC: as implicit reference conversion. if (destination.IsReferenceType) { return(ConversionKind.ImplicitReference); } // SPEC: The set of implicit conversions is extended to include... // SPEC: ... from the null literal to any pointer type. if (destination is PointerTypeSymbol) { return(ConversionKind.NullToPointer); } return(ConversionKind.NoConversion); }
/// <summary> /// Returns the type to be used as a field type; generates errors in case the type is not /// supported for anonymous type fields. /// </summary> private TypeSymbol GetAnonymousTypeFieldType(BoundExpression expression, CSharpSyntaxNode errorSyntax, DiagnosticBag diagnostics, ref bool hasError) { object errorArg = null; TypeSymbol expressionType = expression.Type; if (!expression.HasAnyErrors) { if (expression.HasExpressionType()) { if (expressionType.SpecialType == SpecialType.System_Void) { errorArg = expressionType; expressionType = CreateErrorType(SyntaxFacts.GetText(SyntaxKind.VoidKeyword)); } else if (expressionType.IsUnsafe()) { errorArg = expressionType; // CONSIDER: we could use an explicit error type instead of the unsafe type. } else if (expressionType.IsRestrictedType()) { errorArg = expressionType; } } else { if (expression.Kind == BoundKind.UnboundLambda) { errorArg = ((UnboundLambda)expression).MessageID.Localize(); } else if (expression.Kind == BoundKind.MethodGroup) { errorArg = MessageID.IDS_MethodGroup.Localize(); } else { Debug.Assert(expression.IsLiteralNull(), "How did we successfully bind an expression without a type?"); errorArg = MessageID.IDS_NULL.Localize(); } } } if ((object)expressionType == null) { expressionType = CreateErrorType("error"); } if (errorArg != null) { hasError = true; Error(diagnostics, ErrorCode.ERR_AnonymousTypePropertyAssignedBadValue, errorSyntax, errorArg); // NOTE: ERR_QueryRangeVariableAssignedBadValue is being generated // by query binding code and never reach this point } return expressionType; }
private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSymbol methodOpt, TypeSymbol type, BoundExpression left, BoundExpression right) { bool isChecked, isLifted, requiresLifted; string opName = GetBinaryOperatorName(opKind, out isChecked, out isLifted, out requiresLifted); // 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 (opKind.OperandTypes()) { case BinaryOperatorKind.Enum: case BinaryOperatorKind.EnumAndUnderlying: case BinaryOperatorKind.UnderlyingAndEnum: { var enumOperand = (opKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left; var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType()); if (opKind.IsLifted()) { promotedType = _nullableType.Construct(promotedType); } loweredLeft = PromoteEnumOperand(left, loweredLeft, promotedType, isChecked); loweredRight = PromoteEnumOperand(right, loweredRight, promotedType, isChecked); var result = MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight); return Demote(result, type, isChecked); } default: return MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight); } }
private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, DiagnosticBag diagnostics, BoundExpression left, BoundExpression right, ref int compoundStringLength) { BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); // If either operand is bad, don't try to do binary operator overload resolution; that will just // make cascading errors. if (left.HasAnyErrors || right.HasAnyErrors) { // NOTE: no user-defined conversion candidates return new BoundBinaryOperator(node, kind, left, right, ConstantValue.NotAvailable, null, LookupResultKind.Empty, GetBinaryOperatorErrorType(kind, diagnostics, node), true); } TypeSymbol leftType = left.Type; TypeSymbol rightType = right.Type; if ((object)leftType != null && leftType.IsDynamic() || (object)rightType != null && rightType.IsDynamic()) { return BindDynamicBinaryOperator(node, kind, left, right, diagnostics); } // SPEC OMISSION: The C# 2.0 spec had a line in it that noted that the expressions "null == null" // SPEC OMISSION: and "null != null" were to be automatically treated as the appropriate constant; // SPEC OMISSION: overload resolution was to be skipped. That's because a strict reading // SPEC OMISSION: of the overload resolution spec shows that overload resolution would give an // SPEC OMISSION: ambiguity error for this case; the expression is ambiguous between the int?, // SPEC OMISSION: bool? and string versions of equality. This line was accidentally edited // SPEC OMISSION: out of the C# 3 specification; we should re-insert it. bool leftNull = left.IsLiteralNull(); bool rightNull = right.IsLiteralNull(); bool isEquality = kind == BinaryOperatorKind.Equal || kind == BinaryOperatorKind.NotEqual; if (isEquality && leftNull && rightNull) { return new BoundLiteral(node, ConstantValue.Create(kind == BinaryOperatorKind.Equal), GetSpecialType(SpecialType.System_Boolean, diagnostics, node)); } // SPEC: For an operation of one of the forms x == null, null == x, x != null, null != x, // SPEC: where x is an expression of nullable type, if operator overload resolution // SPEC: fails to find an applicable operator, the result is instead computed from // SPEC: the HasValue property of x. // Note that the spec says "fails to find an applicable operator", not "fails to // find a unique best applicable operator." For example: // struct X { // public static bool operator ==(X? x, double? y) {...} // public static bool operator ==(X? x, decimal? y) {...} // // The comparison "x == null" should produce an ambiguity error rather // that being bound as !x.HasValue. // LookupResultKind resultKind; ImmutableArray<MethodSymbol> originalUserDefinedOperators; var best = this.BinaryOperatorOverloadResolution(kind, left, right, node, diagnostics, out resultKind, out originalUserDefinedOperators); // However, as an implementation detail, we never "fail to find an applicable // operator" during overload resolution if we have x == null, etc. We always // find at least the reference conversion object == object; the overload resolution // code does not reject that. Therefore what we should do is only bind // "x == null" as a nullable-to-null comparison if overload resolution chooses // the reference conversion. BoundExpression resultLeft = left; BoundExpression resultRight = right; MethodSymbol resultMethod = null; ConstantValue resultConstant = null; BinaryOperatorKind resultOperatorKind; TypeSymbol resultType; bool hasErrors; if (!best.HasValue) { resultOperatorKind = kind; resultType = CreateErrorType(); hasErrors = true; } else { var signature = best.Signature; bool isObjectEquality = signature.Kind == BinaryOperatorKind.ObjectEqual || signature.Kind == BinaryOperatorKind.ObjectNotEqual; bool isNullableEquality = (object)signature.Method == null && (signature.Kind.Operator() == BinaryOperatorKind.Equal || signature.Kind.Operator() == BinaryOperatorKind.NotEqual) && (leftNull && (object)rightType != null && rightType.IsNullableType() || rightNull && (object)leftType != null && leftType.IsNullableType()); if (isNullableEquality) { resultOperatorKind = kind | BinaryOperatorKind.NullableNull; resultType = GetSpecialType(SpecialType.System_Boolean, diagnostics, node); hasErrors = false; } else { resultOperatorKind = signature.Kind; resultType = signature.ReturnType; resultMethod = signature.Method; resultLeft = CreateConversion(left, best.LeftConversion, signature.LeftType, diagnostics); resultRight = CreateConversion(right, best.RightConversion, signature.RightType, diagnostics); resultConstant = FoldBinaryOperator(node, resultOperatorKind, resultLeft, resultRight, resultType.SpecialType, diagnostics, ref compoundStringLength); HashSet<DiagnosticInfo> useSiteDiagnostics = null; hasErrors = isObjectEquality && !BuiltInOperators.IsValidObjectEquality(Conversions, leftType, leftNull, rightType, rightNull, ref useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics); } } if (hasErrors) { ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, resultKind); resultOperatorKind &= ~BinaryOperatorKind.TypeMask; } switch (node.Kind()) { case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: case SyntaxKind.LessThanExpression: case SyntaxKind.LessThanOrEqualExpression: case SyntaxKind.GreaterThanExpression: case SyntaxKind.GreaterThanOrEqualExpression: break; default: if (leftType.IsVoidPointer() || rightType.IsVoidPointer()) { // CONSIDER: dev10 cascades this, but roslyn doesn't have to. Error(diagnostics, ErrorCode.ERR_VoidError, node); hasErrors = true; } break; } hasErrors = hasErrors || resultConstant != null && resultConstant.IsBad; return new BoundBinaryOperator( node, resultOperatorKind.WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), resultLeft, resultRight, resultConstant, resultMethod, resultKind, originalUserDefinedOperators, resultType, hasErrors); }
private static bool IsLegalDynamicOperand(BoundExpression operand) { Debug.Assert(operand != null); TypeSymbol type = operand.Type; // Literal null is a legal operand to a dynamic operation. The other typeless expressions -- // method groups, lambdas, anonymous methods -- are not. // If the operand is of a class, interface, delegate, array, struct, enum, nullable // or type param types, it's legal to use in a dynamic expression. In short, the type // must be one that is convertible to object. if ((object)type == null) { return operand.IsLiteralNull(); } // Pointer types and very special types are not convertible to object. return !type.IsPointerType() && !type.IsRestrictedType() && type.SpecialType != SpecialType.System_Void; }
private BoundExpression RewriteNullableNullEquality( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol returnType) { // This handles the case where we have a nullable user-defined struct type compared against null, eg: // // struct S {} ... S? s = whatever; if (s != null) // // If S does not define an overloaded != operator then this is lowered to s.HasValue. // // If the type already has a user-defined or built-in operator then comparing to null is // treated as a lifted equality operator. Debug.Assert(loweredLeft != null); Debug.Assert(loweredRight != null); Debug.Assert((object)returnType != null); Debug.Assert(returnType.SpecialType == SpecialType.System_Boolean); Debug.Assert(loweredLeft.IsLiteralNull() != loweredRight.IsLiteralNull()); BoundExpression nullable = loweredRight.IsLiteralNull() ? loweredLeft : loweredRight; // If the other side is known to always be null then we can simply generate true or false, as appropriate. if (NullableNeverHasValue(nullable)) { return MakeLiteral(syntax, ConstantValue.Create(kind == BinaryOperatorKind.NullableNullEqual), returnType); } BoundExpression nonNullValue = NullableAlwaysHasValue(nullable); if (nonNullValue != null) { // We have something like "if (new int?(M()) != null)". We can optimize this to // evaluate M() for its side effects and then result in true or false, as appropriate. // TODO: If the expression has no side effects then it can be optimized away here as well. return new BoundSequence( syntax: syntax, locals: ImmutableArray<LocalSymbol>.Empty, sideEffects: ImmutableArray.Create<BoundExpression>(nonNullValue), value: MakeBooleanConstant(syntax, kind == BinaryOperatorKind.NullableNullNotEqual), type: returnType); } // arr?.Length == null var conditionalAccess = nullable as BoundLoweredConditionalAccess; if (conditionalAccess != null && (conditionalAccess.WhenNullOpt == null || conditionalAccess.WhenNullOpt.IsDefaultValue())) { BoundExpression whenNotNull = RewriteNullableNullEquality( syntax, kind, conditionalAccess.WhenNotNull, loweredLeft.IsLiteralNull() ? loweredLeft : loweredRight, returnType); var whenNull = kind == BinaryOperatorKind.NullableNullEqual ? MakeBooleanConstant(syntax, true) : null; return conditionalAccess.Update(conditionalAccess.Receiver, conditionalAccess.HasValueMethodOpt, whenNotNull, whenNull, conditionalAccess.Id, whenNotNull.Type); } BoundExpression call = MakeNullableHasValue(syntax, nullable); BoundExpression result = kind == BinaryOperatorKind.NullableNullNotEqual ? call : new BoundUnaryOperator(syntax, UnaryOperatorKind.BoolLogicalNegation, call, ConstantValue.NotAvailable, null, LookupResultKind.Viable, returnType); return result; }
protected void GenerateImplicitConversionError(DiagnosticBag diagnostics, CSharpSyntaxNode syntax, Conversion conversion, BoundExpression expression, TypeSymbol targetType) { Debug.Assert(expression != null); Debug.Assert((object)targetType != null); if (targetType.TypeKind == TypeKind.Error) { return; } if (expression.Kind == BoundKind.BadExpression) { return; } if (expression.Kind == BoundKind.UnboundLambda) { GenerateAnonymousFunctionConversionError(diagnostics, syntax, (UnboundLambda)expression, targetType); return; } var sourceType = expression.Type; if ((object)sourceType != null) { GenerateImplicitConversionError(diagnostics, this.Compilation, syntax, conversion, sourceType, targetType, expression.ConstantValue); return; } if (expression.IsLiteralNull()) { if (targetType.TypeKind == TypeKind.TypeParameter) { Error(diagnostics, ErrorCode.ERR_TypeVarCantBeNull, syntax, targetType); return; } if (targetType.IsValueType) { Error(diagnostics, ErrorCode.ERR_ValueCantBeNull, syntax, targetType); return; } } if (expression.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)expression; if (!Conversions.ReportDelegateMethodGroupDiagnostics(this, methodGroup, targetType, diagnostics)) { var nodeForSquiggle = syntax; while (nodeForSquiggle.Kind() == SyntaxKind.ParenthesizedExpression) { nodeForSquiggle = ((ParenthesizedExpressionSyntax)nodeForSquiggle).Expression; } if (nodeForSquiggle.Kind() == SyntaxKind.SimpleMemberAccessExpression || nodeForSquiggle.Kind() == SyntaxKind.PointerMemberAccessExpression) { nodeForSquiggle = ((MemberAccessExpressionSyntax)nodeForSquiggle).Name; } var location = nodeForSquiggle.Location; if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, targetType, location)) { return; } Error(diagnostics, targetType.IsDelegateType() ? ErrorCode.ERR_MethDelegateMismatch : ErrorCode.ERR_MethGrpToNonDel, location, methodGroup.Name, targetType); } return; } Debug.Assert(expression.HasAnyErrors && expression.Kind != BoundKind.UnboundLambda, "Missing a case in implicit conversion error reporting"); }
protected void GenerateImplicitConversionError( DiagnosticBag diagnostics, CSharpSyntaxNode syntax, Conversion conversion, BoundExpression operand, TypeSymbol targetType) { Debug.Assert(operand != null); Debug.Assert((object)targetType != null); if (targetType.TypeKind == TypeKind.Error) { return; } if (operand.Kind == BoundKind.BadExpression) { return; } if (operand.Kind == BoundKind.UnboundLambda) { GenerateAnonymousFunctionConversionError(diagnostics, syntax, (UnboundLambda)operand, targetType); return; } if (operand.Kind == BoundKind.TupleLiteral) { var tuple = (BoundTupleLiteral)operand; var targetElementTypes = default(ImmutableArray<TypeSymbol>); // If target is a tuple or compatible type with the same number of elements, // report errors for tuple arguments that failed to convert, which would be more useful. if (targetType.TryGetElementTypesIfTupleOrCompatible(out targetElementTypes) && targetElementTypes.Length == tuple.Arguments.Length) { GenerateImplicitConversionErrorsForTupleLiteralArguments(diagnostics, tuple.Arguments, targetElementTypes); return; } // target is not compatible with source and source does not have a type if ((object)tuple.Type == null) { Error(diagnostics, ErrorCode.ERR_ConversionNotTupleCompatible, syntax, tuple.Arguments.Length, targetType); return; } // Otherwise it is just a regular conversion failure from T1 to T2. } var sourceType = operand.Type; if ((object)sourceType != null) { GenerateImplicitConversionError(diagnostics, this.Compilation, syntax, conversion, sourceType, targetType, operand.ConstantValue); return; } if (operand.IsLiteralNull()) { if (targetType.TypeKind == TypeKind.TypeParameter) { Error(diagnostics, ErrorCode.ERR_TypeVarCantBeNull, syntax, targetType); return; } if (targetType.IsValueType) { Error(diagnostics, ErrorCode.ERR_ValueCantBeNull, syntax, targetType); return; } } if (operand.Kind == BoundKind.MethodGroup) { var methodGroup = (BoundMethodGroup)operand; if (!Conversions.ReportDelegateMethodGroupDiagnostics(this, methodGroup, targetType, diagnostics)) { var nodeForSquiggle = syntax; while (nodeForSquiggle.Kind() == SyntaxKind.ParenthesizedExpression) { nodeForSquiggle = ((ParenthesizedExpressionSyntax)nodeForSquiggle).Expression; } if (nodeForSquiggle.Kind() == SyntaxKind.SimpleMemberAccessExpression || nodeForSquiggle.Kind() == SyntaxKind.PointerMemberAccessExpression) { nodeForSquiggle = ((MemberAccessExpressionSyntax)nodeForSquiggle).Name; } var location = nodeForSquiggle.Location; if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, targetType, location)) { return; } Error(diagnostics, targetType.IsDelegateType() ? ErrorCode.ERR_MethDelegateMismatch : ErrorCode.ERR_MethGrpToNonDel, location, methodGroup.Name, targetType); } return; } Debug.Assert(operand.HasAnyErrors && operand.Kind != BoundKind.UnboundLambda, "Missing a case in implicit conversion error reporting"); }
public BoundExpression Convert(TypeSymbol type, BoundExpression arg, Conversion conversion, bool isChecked = false) { // NOTE: We can see user-defined conversions at this point because there are places in the bound tree where // the binder stashes Conversion objects for later consumption (e.g. foreach, nullable, increment). if ((object)conversion.Method != null && conversion.Method.Parameters[0].Type != arg.Type) { arg = Convert(conversion.Method.Parameters[0].Type, arg); } if (conversion.Kind == ConversionKind.ImplicitReference && arg.IsLiteralNull()) { return Null(type); } return new BoundConversion(Syntax, arg, conversion, isChecked, true, null, type) { WasCompilerGenerated = true }; }
private static bool NullableNeverHasValue(BoundExpression expression) { // CONSIDER: A sequence of side effects with an always-null expression as its value // CONSIDER: can be optimized also. Should we? if (expression.IsLiteralNull()) { return true; } if (!expression.Type.IsNullableType()) { return false; } // default(int?) never has a value. if (expression.Kind == BoundKind.DefaultOperator) { return true; } // new int?() never has a value if there is no argument. if (expression.Kind == BoundKind.ObjectCreationExpression) { if (((BoundObjectCreationExpression)expression).Arguments.Length == 0) { return true; } } return false; }
/// <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); }
private bool IsOperandErrors(CSharpSyntaxNode node, ref BoundExpression operand, DiagnosticBag diagnostics) { switch (operand.Kind) { case BoundKind.UnboundLambda: case BoundKind.Lambda: case BoundKind.MethodGroup: // New in Roslyn - see DevDiv #864740. // operand for an is or as expression cannot be a lambda expression or method group if (!operand.HasAnyErrors) { Error(diagnostics, ErrorCode.ERR_LambdaInIsAs, node); operand = BadExpression(node, operand); } return true; default: if ((object)operand.Type == null && !operand.IsLiteralNull()) { if (!operand.HasAnyErrors) { // Operator 'is' cannot be applied to operand of type '(int, <null>)' Error(diagnostics, ErrorCode.ERR_BadUnaryOp, node, SyntaxFacts.GetText(SyntaxKind.IsKeyword), operand.Display); } return true; } break; } return operand.HasAnyErrors; }
private static ConversionKind ClassifyNullLiteralConversion(BoundExpression source, TypeSymbol destination) { Debug.Assert((object)source != null); Debug.Assert((object)destination != null); if (!source.IsLiteralNull()) { return ConversionKind.NoConversion; } // SPEC: An implicit conversion exists from the null literal to any nullable type. if (destination.IsNullableType()) { // The spec defines a "null literal conversion" specifically as a conversion from // null to nullable type. return ConversionKind.NullLiteral; } // SPEC: An implicit conversion exists from the null literal to any reference type. // SPEC: An implicit conversion exists from the null literal to type parameter T, // SPEC: provided T is known to be a reference type. [...] The conversion [is] classified // SPEC: as implicit reference conversion. if (destination.IsReferenceType) { return ConversionKind.ImplicitReference; } // SPEC: The set of implicit conversions is extended to include... // SPEC: ... from the null literal to any pointer type. if (destination is PointerTypeSymbol) { return ConversionKind.NullToPointer; } return ConversionKind.NoConversion; }
private bool UseOnlyReferenceEquality(BoundExpression left, BoundExpression right, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { return BuiltInOperators.IsValidObjectEquality(Conversions, left.Type, left.IsLiteralNull(), right.Type, right.IsLiteralNull(), ref useSiteDiagnostics) && ((object)left.Type == null || (!left.Type.IsDelegateType() && left.Type.SpecialType != SpecialType.System_String && left.Type.SpecialType != SpecialType.System_Delegate)) && ((object)right.Type == null || (!right.Type.IsDelegateType() && right.Type.SpecialType != SpecialType.System_String && right.Type.SpecialType != SpecialType.System_Delegate)); }
public static bool IsLiteralNullOrDefault(this BoundExpression node) { return(node.IsLiteralNull() || node.IsLiteralDefault()); }
private BoundExpression RewriteDelegateOperation(CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, SpecialMember member) { MethodSymbol method; if (operatorKind == BinaryOperatorKind.DelegateEqual || operatorKind == BinaryOperatorKind.DelegateNotEqual) { method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(member); if (loweredRight.IsLiteralNull() || loweredLeft.IsLiteralNull() || (object)(method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(member)) == null) { // use reference equality in the absence of overloaded operators for System.Delegate. operatorKind = (operatorKind & (~BinaryOperatorKind.Delegate)) | BinaryOperatorKind.Object; return new BoundBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, default(ConstantValue), null, LookupResultKind.Empty, type); } } else { method = GetSpecialTypeMethod(syntax, member); } Debug.Assert((object)method != null); BoundExpression call = _inExpressionLambda ? new BoundBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, null, method, default(LookupResultKind), method.ReturnType) : (BoundExpression)BoundCall.Synthesized(syntax, null, method, loweredLeft, loweredRight); BoundExpression result = method.ReturnType.SpecialType == SpecialType.System_Delegate ? MakeConversionNode(syntax, call, Conversion.ExplicitReference, type, @checked: false) : call; return result; }
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)); } }