internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, TypeSymbol rightType, bool rightIsNull, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // SPEC: The predefined reference type equality operators require one of the following: // SPEC: (1) Both operands are a value of a type known to be a reference-type or the literal null. // SPEC: Furthermore, an explicit reference conversion exists from the type of either // SPEC: operand to the type of the other operand. Or: // SPEC: (2) One operand is a value of type T where T is a type-parameter and the other operand is // SPEC: the literal null. Furthermore T does not have the value type constraint. // SPEC ERROR: Notice that the spec calls out that an explicit reference conversion must exist; // SPEC ERROR: in fact it should say that an explicit reference conversion, implicit reference // SPEC ERROR: conversion or identity conversion must exist. The conversion from object to object // SPEC ERROR: is not classified as a reference conversion at all; it is an identity conversion. // Dev10 does not follow the spec exactly for type parameters. Specifically, in Dev10, // if a type parameter argument is known to be a value type, or if a type parameter // argument is not known to be either a value type or reference type and the other // argument is not null, reference type equality cannot be applied. Otherwise, the // effective base class of the type parameter is used to determine the conversion // to the other argument type. (See ExpressionBinder::GetRefEqualSigs.) if (((object)leftType != null) && leftType.IsTypeParameter()) { if (leftType.IsValueType || (!leftType.IsReferenceType && !rightIsNull)) { return(false); } leftType = ((TypeParameterSymbol)leftType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)leftType != null); } if (((object)rightType != null) && rightType.IsTypeParameter()) { if (rightType.IsValueType || (!rightType.IsReferenceType && !leftIsNull)) { return(false); } rightType = ((TypeParameterSymbol)rightType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)rightType != null); } var leftIsReferenceType = ((object)leftType != null) && leftType.IsReferenceType; if (!leftIsReferenceType && !leftIsNull) { return(false); } var rightIsReferenceType = ((object)rightType != null) && rightType.IsReferenceType; if (!rightIsReferenceType && !rightIsNull) { return(false); } // If at least one side is null then clearly a conversion exists. if (leftIsNull || rightIsNull) { return(true); } var leftConversion = Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics); if (leftConversion.IsIdentity || leftConversion.IsReference) { return(true); } var rightConversion = Conversions.ClassifyConversion(rightType, leftType, ref useSiteDiagnostics); if (rightConversion.IsIdentity || rightConversion.IsReference) { return(true); } return(false); }
internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, TypeSymbol rightType, bool rightIsNull, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // SPEC: The predefined reference type equality operators require one of the following: // SPEC: (1) Both operands are a value of a type known to be a reference-type or the literal null. // SPEC: Furthermore, an explicit reference conversion exists from the type of either // SPEC: operand to the type of the other operand. Or: // SPEC: (2) One operand is a value of type T where T is a type-parameter and the other operand is // SPEC: the literal null. Furthermore T does not have the value type constraint. // SPEC ERROR: Notice that the spec calls out that an explicit reference conversion must exist; // SPEC ERROR: in fact it should say that an explicit reference conversion, implicit reference // SPEC ERROR: conversion or identity conversion must exist. The conversion from object to object // SPEC ERROR: is not classified as a reference conversion at all; it is an identity conversion. // Dev10 does not follow the spec exactly for type parameters. Specifically, in Dev10, // if a type parameter argument is known to be a value type, or if a type parameter // argument is not known to be either a value type or reference type and the other // argument is not null, reference type equality cannot be applied. Otherwise, the // effective base class of the type parameter is used to determine the conversion // to the other argument type. (See ExpressionBinder::GetRefEqualSigs.) if (((object)leftType != null) && leftType.IsTypeParameter()) { if (leftType.IsValueType || (!leftType.IsReferenceType && !rightIsNull)) { return false; } leftType = ((TypeParameterSymbol)leftType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)leftType != null); } if (((object)rightType != null) && rightType.IsTypeParameter()) { if (rightType.IsValueType || (!rightType.IsReferenceType && !leftIsNull)) { return false; } rightType = ((TypeParameterSymbol)rightType).EffectiveBaseClass(ref useSiteDiagnostics); Debug.Assert((object)rightType != null); } var leftIsReferenceType = ((object)leftType != null) && leftType.IsReferenceType; if (!leftIsReferenceType && !leftIsNull) { return false; } var rightIsReferenceType = ((object)rightType != null) && rightType.IsReferenceType; if (!rightIsReferenceType && !rightIsNull) { return false; } // If at least one side is null then clearly a conversion exists. if (leftIsNull || rightIsNull) { return true; } var leftConversion = Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics); if (leftConversion.IsIdentity || leftConversion.IsReference) { return true; } var rightConversion = Conversions.ClassifyConversion(rightType, leftType, ref useSiteDiagnostics); if (rightConversion.IsIdentity || rightConversion.IsReference) { return true; } return false; }