Beispiel #1
0
        /// <remarks>
        /// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification.
        /// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
        /// </remarks>
        public static TypeSymbol InferBestType(ImmutableArray<BoundExpression> exprs, ConversionsBase conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC:    7.5.2.14 Finding the best common type of a set of expressions
            // SPEC:    In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and
            // SPEC:    the return types of anonymous functions with block bodies are found in this way.
            // SPEC:    Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method:
            // SPEC:        T M<X>(X x1 … X xm)
            // SPEC:    with the Ei as arguments. 
            // SPEC:    More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei to X.
            // SPEC:    Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions.
            // SPEC:    If no such S exists, the expressions have no best common type.

            // All non-null types are candidates for best type inference.
            HashSet<TypeSymbol> candidateTypes = new HashSet<TypeSymbol>();
            foreach (BoundExpression expr in exprs)
            {
                TypeSymbol type = expr.Type;

                if ((object)type != null)
                {
                    if (type.IsErrorType())
                    {
                        hadMultipleCandidates = false;
                        return type;
                    }

                    candidateTypes.Add(type);
                }
            }

            hadMultipleCandidates = candidateTypes.Count > 1;

            // Perform best type inference on candidate types.
            return InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, ref useSiteDiagnostics);
        }
Beispiel #2
0
        /// <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, 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();

            TypeSymbol type1 = expr1.Type;

            if ((object)type1 != null)
            {
                if (type1.IsErrorType())
                {
                    candidateTypes.Free();
                    hadMultipleCandidates = false;
                    return type1;
                }

                if (conversions.ClassifyImplicitConversionFromExpression(expr2, type1, ref useSiteDiagnostics).Exists)
                {
                    candidateTypes.Add(type1);
                }
            }

            TypeSymbol type2 = expr2.Type;

            if ((object)type2 != null && type2 != type1)
            {
                if (type2.IsErrorType())
                {
                    candidateTypes.Free();
                    hadMultipleCandidates = false;
                    return type2;
                }

                if (conversions.ClassifyImplicitConversionFromExpression(expr1, type2, ref useSiteDiagnostics).Exists)
                {
                    candidateTypes.Add(type2);
                }
            }

            hadMultipleCandidates = candidateTypes.Count > 1;

            return InferBestType(candidateTypes.ToImmutableAndFree(), conversions, ref useSiteDiagnostics);
        }
Beispiel #3
0
        /// <remarks>
        /// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification.
        /// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
        /// </remarks>
        public static TypeSymbol InferBestType(
            ImmutableArray <BoundExpression> exprs,
            ConversionsBase conversions,
            out bool hadNullabilityMismatch,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC:    7.5.2.14 Finding the best common type of a set of expressions
            // SPEC:    In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and
            // SPEC:    the return types of anonymous functions with block bodies are found in this way.
            // SPEC:    Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method:
            // SPEC:        T M<X>(X x1 … X xm)
            // SPEC:    with the Ei as arguments.
            // SPEC:    More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei to X.
            // SPEC:    Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions.
            // SPEC:    If no such S exists, the expressions have no best common type.

            // All non-null types are candidates for best type inference.
            IEqualityComparer <TypeSymbol> comparer       = conversions.IncludeNullability ? TypeSymbol.EqualsConsiderEverything : TypeSymbol.EqualsIgnoringNullableComparer;
            HashSet <TypeSymbol>           candidateTypes = new HashSet <TypeSymbol>(comparer);

            foreach (BoundExpression expr in exprs)
            {
                TypeSymbol type = expr.Type;

                if ((object)type != null)
                {
                    if (type.IsErrorType())
                    {
                        hadNullabilityMismatch = false;
                        return(type);
                    }

                    if (conversions.IncludeNullability)
                    {
                        type = type.SetSpeakableNullabilityForReferenceTypes();
                    }
                    candidateTypes.Add(type);
                }
            }

            // Perform best type inference on candidate types.
            var builder = ArrayBuilder <TypeSymbol> .GetInstance(candidateTypes.Count);

            builder.AddRange(candidateTypes);
            var result = GetBestType(builder, conversions, out hadNullabilityMismatch, ref useSiteDiagnostics);

            builder.Free();
            return(result);
        }
Beispiel #4
0
        /// <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,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // 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(type1.MergeEquivalentTypes(type2, VarianceKind.Out));
                }

                return(null);
            }

            if (t1tot2)
            {
                return(type2);
            }

            if (t2tot1)
            {
                return(type1);
            }

            return(null);
        }
Beispiel #5
0
        /// <summary>
        /// Does an expression of type <paramref name="expressionType"/> "match" a pattern that looks for
        /// type <paramref name="patternType"/>?
        /// 'true' if the matched type catches all of them, 'false' if it catches none of them, and
        /// 'null' if it might catch some of them. For this test we assume the expression's value
        /// isn't null.
        /// </summary>
        protected bool?ExpressionOfTypeMatchesPatternType(
            TypeSymbol expressionType,
            TypeSymbol patternType,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            if (expressionType == patternType)
            {
                return(true);
            }

            var conversion = _conversions.ClassifyBuiltInConversion(expressionType, patternType, ref useSiteDiagnostics);

            // This is for classification purposes only; we discard use-site diagnostics. Use-site diagnostics will
            // be given if a conversion is actually used.
            switch (conversion.Kind)
            {
            case ConversionKind.Boxing:                 // a value of type int matches a pattern of type object
            case ConversionKind.Identity:               // a value of a given type matches a pattern of that type
            case ConversionKind.ImplicitReference:      // a value of type string matches a pattern of type object
                return(true);

            case ConversionKind.ImplicitNullable:       // a value of type int matches a pattern of type int?
            case ConversionKind.ExplicitNullable:       // a non-null value of type "int?" matches a pattern of type int
                // but if the types differ (e.g. one of them is type byte and the other is type int?).. no match
                return(ConversionsBase.HasIdentityConversion(expressionType.StrippedType().TupleUnderlyingTypeOrSelf(), patternType.StrippedType().TupleUnderlyingTypeOrSelf()));

            case ConversionKind.ExplicitEnumeration:    // a value of enum type does not match a pattern of integral type
            case ConversionKind.ExplicitNumeric:        // a value of type long does not match a pattern of type int
            case ConversionKind.ImplicitNumeric:        // a value of type short does not match a pattern of type int
            case ConversionKind.ImplicitTuple:          // distinct tuple types don't match
            case ConversionKind.NoConversion:
                return(false);

            case ConversionKind.ExplicitDynamic:        // a value of type dynamic might not match a pattern of type other than object
            case ConversionKind.ExplicitReference:      // a narrowing reference conversion might or might not succeed
            case ConversionKind.Unboxing:               // a value of type object might match a pattern of type int
                return(null);

            default:
                // other conversions don't apply (e.g. conversions from expression, user-defined) and should not arise
                throw ExceptionUtilities.UnexpectedValue(conversion.Kind);
            }
        }
Beispiel #6
0
        /// <remarks>
        /// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification.
        /// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
        /// </remarks>
        public static TypeSymbol?InferBestType(
            ImmutableArray <BoundExpression> exprs,
            ConversionsBase conversions,
            ref HashSet <DiagnosticInfo>?useSiteDiagnostics)
        {
            // SPEC:    7.5.2.14 Finding the best common type of a set of expressions
            // SPEC:    In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and
            // SPEC:    the return types of anonymous functions with block bodies are found in this way.
            // SPEC:    Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method:
            // SPEC:        T M<X>(X x1 … X xm)
            // SPEC:    with the Ei as arguments.
            // SPEC:    More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei to X.
            // SPEC:    Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions.
            // SPEC:    If no such S exists, the expressions have no best common type.

            // All non-null types are candidates for best type inference.
            IEqualityComparer <TypeSymbol> comparer       = conversions.IncludeNullability ? Symbols.SymbolEqualityComparer.ConsiderEverything : Symbols.SymbolEqualityComparer.IgnoringNullable;
            HashSet <TypeSymbol>           candidateTypes = new HashSet <TypeSymbol>(comparer);

            foreach (BoundExpression expr in exprs)
            {
                TypeSymbol?type = expr.Type;

                if (type is { })
Beispiel #7
0
        /// <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.IsDynamic())
                {
                    return(type1);
                }

                if (type2.IsDynamic())
                {
                    return(type2);
                }

                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);
        }
        ////////////////////////////////////////////////////////////////////////////////
        //
        // Helper methods
        //


        ////////////////////////////////////////////////////////////////////////////////
        //
        // In error recovery and reporting scenarios we sometimes end up in a situation
        // like this:
        //
        // x.Foo( y=>
        //
        // and the question is, "is Foo a valid extension method of x?"  If Foo is
        // generic, then Foo will be something like:
        //
        // static Blah Foo<T>(this Bar<T> bar, Func<T, T> f){ ... }
        //
        // What we would like to know is: given _only_ the expression x, can we infer
        // what T is in Bar<T> ?  If we can, then for error recovery and reporting
        // we can provisionally consider Foo to be an extension method of x. If we 
        // cannot deduce this just from x then we should consider Foo to not be an
        // extension method of x, at least until we have more information.
        //
        // Clearly it is pointless to run multiple phases
        public static ImmutableArray<TypeSymbol> InferTypeArgumentsFromFirstArgument(
            ConversionsBase conversions,
            MethodSymbol method,
            ImmutableArray<BoundExpression> arguments,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert((object)method != null);
            Debug.Assert(method.Arity > 0);
            Debug.Assert(!arguments.IsDefault);
            // We need at least one formal parameter type and at least one argument.
            if ((method.ParameterCount < 1) || (arguments.Length < 1))
            {
                return default(ImmutableArray<TypeSymbol>);
            }

            Debug.Assert(!method.ParameterTypes[0].IsDynamic());

            var constructedFromMethod = method.ConstructedFrom;

            var inferrer = new MethodTypeInferrer(
                conversions,
                constructedFromMethod.TypeParameters,
                constructedFromMethod.ContainingType,
                constructedFromMethod.GetParameterTypes(),
                constructedFromMethod.ParameterRefKinds,
                arguments);

            if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics))
            {
                return default(ImmutableArray<TypeSymbol>);
            }

            return inferrer.GetInferredTypeArguments();
        }
        ////////////////////////////////////////////////////////////////////////////////
        //
        // Fixed, unfixed and bounded type parameters
        //
        // SPEC: During the process of inference each type parameter is either fixed to
        // SPEC: a particular type or unfixed with an associated set of bounds. Each of
        // SPEC: the bounds is of some type T. Initially each type parameter is unfixed
        // SPEC: with an empty set of bounds.

        private MethodTypeInferrer(
            ConversionsBase conversions,
            ImmutableArray<TypeParameterSymbol> methodTypeParameters,
            NamedTypeSymbol constructedContainingTypeOfMethod,
            ImmutableArray<TypeSymbol> formalParameterTypes,
            ImmutableArray<RefKind> formalParameterRefKinds,
            ImmutableArray<BoundExpression> arguments)
        {
            _conversions = conversions;
            _methodTypeParameters = methodTypeParameters;
            _constructedContainingTypeOfMethod = constructedContainingTypeOfMethod;
            _formalParameterTypes = formalParameterTypes;
            _formalParameterRefKinds = formalParameterRefKinds;
            _arguments = arguments;
            _fixedResults = new TypeSymbol[methodTypeParameters.Length];
            _exactBounds = new HashSet<TypeSymbol>[methodTypeParameters.Length];
            _upperBounds = new HashSet<TypeSymbol>[methodTypeParameters.Length];
            _lowerBounds = new HashSet<TypeSymbol>[methodTypeParameters.Length];
            _dependencies = null;
            _dependenciesDirty = false;
        }
 internal LazyNullableContraintChecksDiagnosticInfo(NamedTypeSymbol type, ConversionsBase conversions, Compilation compilation)
 {
     _type        = type;
     _conversions = conversions;
     _compilation = compilation;
 }
Beispiel #11
0
 private BestTypeInferrer(ConversionsBase conversions)
 {
     _conversions = conversions;
 }
Beispiel #12
0
        /// <remarks>
        /// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification.
        /// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
        /// </remarks>
        public static TypeSymbol InferBestType(ImmutableArray <BoundExpression> exprs, ConversionsBase conversions, out bool hadMultipleCandidates, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC:    7.5.2.14 Finding the best common type of a set of expressions
            // SPEC:    In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and
            // SPEC:    the return types of anonymous functions with block bodies are found in this way.
            // SPEC:    Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method:
            // SPEC:        T M<X>(X x1 … X xm)
            // SPEC:    with the Ei as arguments.
            // SPEC:    More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei to X.
            // SPEC:    Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions.
            // SPEC:    If no such S exists, the expressions have no best common type.

            // All non-null types are candidates for best type inference.
            HashSet <TypeSymbol> candidateTypes = new HashSet <TypeSymbol>();

            foreach (BoundExpression expr in exprs)
            {
                TypeSymbol type = expr.Type;

                if ((object)type != null)
                {
                    if (type.IsErrorType())
                    {
                        hadMultipleCandidates = false;
                        return(type);
                    }

                    candidateTypes.Add(type);
                }
            }

            hadMultipleCandidates = candidateTypes.Count > 1;

            // Perform best type inference on candidate types.
            return(InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, ref useSiteDiagnostics));
        }
Beispiel #13
0
        public static TypeSymbol InferBestType(ImmutableArray <TypeSymbol> types, ConversionsBase conversions, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            var inferrer = new BestTypeInferrer(conversions);

            return(inferrer.GetBestType(types, ref useSiteDiagnostics));
        }
Beispiel #14
0
 private BestTypeInferrer(ConversionsBase conversions)
 {
     _conversions = conversions;
 }
Beispiel #15
0
        internal static TypeSymbol GetBestType(
            ArrayBuilder <TypeSymbol> types,
            ConversionsBase conversions,
            out bool hadNullabilityMismatch,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // This code assumes that the types in the list are unique.

            // This code answers the famous Mike Montwill interview question: Can you find the
            // unique best member of a set in O(n) time if the pairwise betterness algorithm
            // might be intransitive?

            // Short-circuit some common cases.
            hadNullabilityMismatch = false;
            switch (types.Count)
            {
            case 0:
                return(null);

            case 1:
                return(types[0]);
            }

            TypeSymbol best      = null;
            int        bestIndex = -1;

            for (int i = 0; i < types.Count; i++)
            {
                TypeSymbol type = types[i];
                if ((object)best == null)
                {
                    best      = type;
                    bestIndex = i;
                }
                else
                {
                    var better = Better(best, type, conversions, out bool hadMismatch, ref useSiteDiagnostics);

                    if ((object)better == null)
                    {
                        best = null;
                        hadNullabilityMismatch = false;
                    }
                    else
                    {
                        if (!better.Equals(best, TypeCompareKind.IgnoreDynamicAndTupleNames))
                        {
                            hadNullabilityMismatch = false;
                        }
                        best = better;
                        hadNullabilityMismatch |= hadMismatch;
                        bestIndex = i;
                    }
                }
            }

            if ((object)best == null)
            {
                hadNullabilityMismatch = false;
                return(null);
            }

            // We have actually only determined that every type *after* best was worse. Now check
            // that every type *before* best was also worse.
            for (int i = 0; i < bestIndex; i++)
            {
                TypeSymbol type   = types[i];
                TypeSymbol better = Better(best, type, conversions, out bool hadMismatch, ref useSiteDiagnostics);
                if (!best.Equals(better, TypeCompareKind.ConsiderEverything))
                {
                    hadNullabilityMismatch = false;
                    return(null);
                }
                hadNullabilityMismatch |= hadMismatch;
            }

            Debug.Assert(!hadNullabilityMismatch || conversions.IncludeNullability);
            return(best);
        }
Beispiel #16
0
        internal static TypeSymbol GetBestType(
            ArrayBuilder <TypeSymbol> types,
            ConversionsBase conversions,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // This code assumes that the types in the list are unique.

            // This code answers the famous Mike Montwill interview question: Can you find the
            // unique best member of a set in O(n) time if the pairwise betterness algorithm
            // might be intransitive?

            // Short-circuit some common cases.
            switch (types.Count)
            {
            case 0:
                return(null);

            case 1:
                return(types[0]);
            }

            TypeSymbol best      = null;
            int        bestIndex = -1;

            for (int i = 0; i < types.Count; i++)
            {
                TypeSymbol type = types[i];
                if ((object)best == null)
                {
                    best      = type;
                    bestIndex = i;
                }
                else
                {
                    var better = Better(best, type, conversions, ref useSiteDiagnostics);

                    if ((object)better == null)
                    {
                        best = null;
                    }
                    else
                    {
                        best      = better;
                        bestIndex = i;
                    }
                }
            }

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

            // We have actually only determined that every type *after* best was worse. Now check
            // that every type *before* best was also worse.
            for (int i = 0; i < bestIndex; i++)
            {
                TypeSymbol type   = types[i];
                TypeSymbol better = Better(best, type, conversions, ref useSiteDiagnostics);
                if (!best.Equals(better, TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
                {
                    return(null);
                }
            }

            return(best);
        }
Beispiel #17
0
 public static TypeSymbol InferBestType(ImmutableArray<TypeSymbol> types, ConversionsBase conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
 {
     var inferrer = new BestTypeInferrer(conversions);
     return inferrer.GetBestType(types, ref useSiteDiagnostics);
 }
Beispiel #18
0
        /// <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,
            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;
                        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;
                        return(type2);
                    }

                    if (conversionsWithoutNullability.ClassifyImplicitConversionFromExpression(expr1, type2, ref useSiteDiagnostics).Exists)
                    {
                        candidateTypes.Add(type2);
                    }
                }

                hadMultipleCandidates = candidateTypes.Count > 1;

                return(GetBestType(candidateTypes, conversions, ref useSiteDiagnostics));
            }
            finally
            {
                candidateTypes.Free();
            }
        }
Beispiel #19
0
 /// <summary>
 /// Perform additional checks after the member has been
 /// added to the member list of the containing type.
 /// </summary>
 internal virtual void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics)
 {
 }