/// <summary> /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names). /// </summary> private static TypeSymbol Better( TypeSymbol type1, TypeSymbol type2, ConversionsBase conversions, out bool hadNullabilityMismatch, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { hadNullabilityMismatch = false; // Anything is better than an error sym. if (type1.IsErrorType()) { return(type2); } if ((object)type2 == null || type2.IsErrorType()) { return(type1); } var conversionsWithoutNullability = conversions.WithNullability(false); var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)) { return(MethodTypeInferrer.Merge( TypeSymbolWithAnnotations.Create(type1), TypeSymbolWithAnnotations.Create(type2), VarianceKind.Out, conversions, out hadNullabilityMismatch).TypeSymbol); } return(null); } if (t1tot2) { return(type2); } if (t2tot1) { return(type1); } return(null); }
/// <remarks> /// This method implements best type inference for the conditional operator ?:. /// NOTE: If either expression is an error type, we return error type as the inference result. /// </remarks> public static TypeSymbol InferBestTypeForConditionalOperator( BoundExpression expr1, BoundExpression expr2, ConversionsBase conversions, out bool hadMultipleCandidates, out bool hadNullabilityMismatch, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // SPEC: The second and third operands, x and y, of the ?: operator control the type of the conditional expression. // SPEC: • If x has type X and y has type Y then // SPEC: o If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression. // SPEC: o If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression. // SPEC: o Otherwise, no expression type can be determined, and a compile-time error occurs. // SPEC: • If only one of x and y has a type, and both x and y, are implicitly convertible to that type, then that is the type of the conditional expression. // SPEC: • Otherwise, no expression type can be determined, and a compile-time error occurs. // A type is a candidate if all expressions are convertible to that type. ArrayBuilder <TypeSymbol> candidateTypes = ArrayBuilder <TypeSymbol> .GetInstance(); try { var conversionsWithoutNullability = conversions.WithNullability(false); TypeSymbol type1 = expr1.Type; if ((object)type1 != null) { if (type1.IsErrorType()) { hadMultipleCandidates = false; hadNullabilityMismatch = false; return(type1); } if (conversionsWithoutNullability.ClassifyImplicitConversionFromExpression(expr2, type1, ref useSiteDiagnostics).Exists) { candidateTypes.Add(type1); } } TypeSymbol type2 = expr2.Type; if ((object)type2 != null) { if (type2.IsErrorType()) { hadMultipleCandidates = false; hadNullabilityMismatch = false; return(type2); } if (conversionsWithoutNullability.ClassifyImplicitConversionFromExpression(expr1, type2, ref useSiteDiagnostics).Exists) { candidateTypes.Add(type2); } } hadMultipleCandidates = candidateTypes.Count > 1; return(GetBestType(candidateTypes, conversions, out hadNullabilityMismatch, ref useSiteDiagnostics)); } finally { candidateTypes.Free(); } }