Example #1
0
        private UserDefinedConversionResult AnalyzeExplicitUserDefinedConversions(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            bool isChecked,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(sourceExpression is null || Compilation is not null);
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);

            // SPEC: A user-defined explicit conversion from type S to type T is processed
            // SPEC: as follows:

            // SPEC: Find the set of types D from which user-defined conversion operators
            // SPEC: will be considered...
            var d = ArrayBuilder <(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)> .GetInstance();

            ComputeUserDefinedExplicitConversionTypeSet(source, target, d, ref useSiteInfo);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U...
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedExplicitConversionSet(sourceExpression, source, target, isChecked: isChecked, d, ubuild, ref useSiteInfo);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // SPEC: If U is empty, the conversion is undefined and a compile-time error occurs.
            if (u.Length == 0)
            {
                return(UserDefinedConversionResult.NoApplicableOperators(u));
            }

            // SPEC: Find the most specific source type SX of the operators in U...
            TypeSymbol sx = MostSpecificSourceTypeForExplicitUserDefinedConversion(u, sourceExpression, source, ref useSiteInfo);

            if ((object)sx == null)
            {
                return(UserDefinedConversionResult.NoBestSourceType(u));
            }

            // SPEC: Find the most specific target type TX of the operators in U...
            TypeSymbol tx = MostSpecificTargetTypeForExplicitUserDefinedConversion(u, target, ref useSiteInfo);

            if ((object)tx == null)
            {
                return(UserDefinedConversionResult.NoBestTargetType(u));
            }

            int?best = MostSpecificConversionOperator(sx, tx, u);

            if (best == null)
            {
                return(UserDefinedConversionResult.Ambiguous(u));
            }

            return(UserDefinedConversionResult.Valid(u, best.Value));
        }
Example #2
0
        /// <remarks>
        /// NOTE: Keep this method in sync with <see cref="AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType"/>.
        /// </remarks>
        private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);

            // User-defined conversions that involve generics can be quite strange. There
            // are two basic problems: first, that generic user-defined conversions can be
            // "shadowed" by built-in conversions, and second, that generic user-defined
            // conversions can make conversions that would never have been legal user-defined
            // conversions if declared non-generically. I call this latter kind of conversion
            // a "suspicious" conversion.
            //
            // The shadowed conversions are easily dealt with:
            //
            // SPEC: If a predefined implicit conversion exists from a type S to type T,
            // SPEC: all user-defined conversions, implicit or explicit, are ignored.
            // SPEC: If a predefined explicit conversion exists from a type S to type T,
            // SPEC: any user-defined explicit conversion from S to T are ignored.
            //
            // The rule above can come into play in cases like:
            //
            // sealed class C<T> { public static implicit operator T(C<T> c) { ... } }
            // C<object> c = whatever;
            // object o = c;
            //
            // The built-in implicit conversion from C<object> to object must shadow
            // the user-defined implicit conversion.
            //
            // The caller of this method checks for user-defined conversions *after*
            // predefined implicit conversions, so we already know that if we got here,
            // there was no predefined implicit conversion.
            //
            // Note that a user-defined *implicit* conversion may win over a built-in
            // *explicit* conversion by the rule given above. That is, if we created
            // an implicit conversion from T to C<T>, then the user-defined implicit
            // conversion from object to C<object> could be valid, even though that
            // would be "replacing" a built-in explicit conversion with a user-defined
            // implicit conversion. This is one of the "suspicious" conversions,
            // as it would not be legal to declare a user-defined conversion from
            // object in a non-generic type.
            //
            // The way the native compiler handles suspicious conversions involving
            // interfaces is neither sensible nor in line with the rules in the
            // specification. It is not clear at this time whether we should be exactly
            // matching the native compiler, the specification, or neither, in Roslyn.

            // Spec (6.4.4 User-defined implicit conversions)
            //   A user-defined implicit conversion from an expression E to type T is processed as follows:

            // SPEC: Find the set of types D from which user-defined conversion operators...
            var d = ArrayBuilder <NamedTypeSymbol> .GetInstance();

            ComputeUserDefinedImplicitConversionTypeSet(source, target, d, ref useSiteDiagnostics);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U...
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedImplicitConversionSet(sourceExpression, source, target, d, ubuild, ref useSiteDiagnostics);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // SPEC: If U is empty, the conversion is undefined and a compile-time error occurs.
            if (u.Length == 0)
            {
                return(UserDefinedConversionResult.NoApplicableOperators(u));
            }

            // SPEC: Find the most specific source type SX of the operators in U...
            TypeSymbol sx = MostSpecificSourceTypeForImplicitUserDefinedConversion(u, source, ref useSiteDiagnostics);

#if XSHARP
            if ((object)sx == null)
            {
                // When converting to USUAL and no valid operator is found then choose the operator with an Object Parameter
                // Except when the source is a pointer type.
                if (this is Conversions)
                {
                    Conversions conv       = this as Conversions;
                    bool        usePointer = source.IsPointerType();
                    if (conv.Compilation.Options.HasRuntime && target == conv.Compilation.UsualType())
                    {
                        for (int i = 0; i < u.Length; i++)
                        {
                            var x = u[i];
                            if (usePointer && x.ToType == target && x.FromType.IsVoidPointer())
                            {
                                return(UserDefinedConversionResult.Valid(u, i));
                            }
                            if (!usePointer && x.ToType == target && x.FromType == conv.Compilation.GetSpecialType(SpecialType.System_Object))
                            {
                                return(UserDefinedConversionResult.Valid(u, i));
                            }
                        }
                    }
                }
            }
#endif
            if ((object)sx == null)
            {
                return(UserDefinedConversionResult.NoBestSourceType(u));
            }

            // SPEC: Find the most specific target type TX of the operators in U...
            TypeSymbol tx = MostSpecificTargetTypeForImplicitUserDefinedConversion(u, target, ref useSiteDiagnostics);
            if ((object)tx == null)
            {
                return(UserDefinedConversionResult.NoBestTargetType(u));
            }

            int?best = MostSpecificConversionOperator(sx, tx, u);
            if (best == null)
            {
                return(UserDefinedConversionResult.Ambiguous(u));
            }

            return(UserDefinedConversionResult.Valid(u, best.Value));
        }
Example #3
0
        private static UserDefinedConversionResult MostSpecificConversionOperatorForSwitchGoverningType(TypeSymbol sx, ImmutableArray <UserDefinedConversionAnalysis> u)
        {
            // This method finds the most specific user-defined implicit conversion operator from the best source type SX to a valid switch governing type.
            // It implements steps (e) and (f) for AnalyzeImplicitUserDefinedConversionForSwitchGoverningType, see comments in that method for details.

            // (e) Instead of finding the most specific target type TX of the operators in U, we consider all unique target types TX for all operators in U.
            //     Let this set of unique target types be called Y.
            var y = new HashSet <TypeSymbol>();
            UserDefinedConversionResult?exactConversionResult = null;

            // (f) Check if there is exactly one target type TX in Y such that it satisfied both conditions below:
            //     (i)  TX is a valid switch governing type as per condition (2) of the SPEC for establishing the switch governing type and
            //     (ii) There is a valid most specific user defined implicit conversion operator from SX to TX.

            foreach (UserDefinedConversionAnalysis analysis in u)
            {
                TypeSymbol tx = analysis.ToType;

                if (y.Add(tx) && tx.IsValidSwitchGoverningType(isTargetTypeOfUserDefinedOp: true))
                {
                    if (!exactConversionResult.HasValue)
                    {
                        // NOTE:    As mentioned in the comments at the start of this function, native compiler doesn't call into
                        // NOTE:    the code for analyzing user defined implicit conversion, i.e. MostSpecificConversionOperator,
                        // NOTE:    but does the analysis of applicable lifted/normal forms itself.
                        // NOTE:    This introduces a SPEC VIOLATION for the following test in the native compiler:

                        // NOTE:    // See test SwitchTests.CS0166_AggregateTypeWithMultipleImplicitConversions_07
                        // NOTE:    struct Conv
                        // NOTE:    {
                        // NOTE:        public static implicit operator int (Conv C) { return 1; }
                        // NOTE:        public static implicit operator int (Conv? C2) { return 0; }
                        // NOTE:        public static int Main()
                        // NOTE:        {
                        // NOTE:            Conv? D = new Conv();
                        // NOTE:            switch(D)
                        // NOTE:            {   ...

                        // SPEC VIOLATION: Native compiler allows the above code to compile
                        // SPEC VIOLATION: even though there are two user-defined implicit conversions:
                        // SPEC VIOLATION: 1) To int type (applicable in normal form): public static implicit operator int (Conv? C2)
                        // SPEC VIOLATION: 2) To int? type (applicable in lifted form): public static implicit operator int (Conv C)

                        // SPEC VIOLATION: We maintain compability with the native compiler.

                        int?best = MostSpecificConversionOperator(sx, tx, u);
                        if (best != null)
                        {
                            exactConversionResult = UserDefinedConversionResult.Valid(u, best.Value);
                            continue;
                        }
                    }

                    return(UserDefinedConversionResult.Ambiguous(u));
                }
            }

            // If there exists such unique TX in Y, then that operator is the resultant user defined conversion and TX is the resultant switch governing type.
            // Otherwise we either have ambiguity or no applicable operators.

            return(exactConversionResult.HasValue ?
                   exactConversionResult.Value :
                   UserDefinedConversionResult.NoApplicableOperators(u));
        }