/// <summary>
        /// Emits the given conversion. 'from' and 'to' matches the classified conversion.
        /// </summary>
        public static void EmitConversion(this CodeGenerator cg, CommonConversion conversion, TypeSymbol from, TypeSymbol to, TypeSymbol op = null, bool @checked = false)
        {
            // {from}, {op} is loaded on stack

            //

            if (conversion.Exists == false)
            {
                throw cg.NotImplementedException($"Conversion from '{from}' to '{to}' ");
            }

            if (conversion.IsIdentity)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                if (to.SpecialType == SpecialType.System_Void)
                {
                    // POP
                    cg.EmitPop(from);
                }

                // nop
            }
            else if (conversion.IsNumeric)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                EmitNumericConversion(cg, from, to, @checked: @checked);
            }
            else if (conversion.IsReference)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                // TODO: ensure from/to is a valid reference type
                cg.EmitCastClass(to);
            }
            else if (conversion.IsUserDefined)
            {
                var method    = (MethodSymbol)conversion.MethodSymbol;
                var ps        = method.Parameters;
                int pconsumed = 0;

                if (method.HasThis)
                {
                    if (from.IsValueType)
                    {
                        if (op != null)
                        {
                            throw new ArgumentException(nameof(op));
                        }

                        cg.EmitStructAddr(from);
                    }
                }
                else
                {
                    if (ps[0].RefKind != RefKind.None)
                    {
                        throw new InvalidOperationException();
                    }
                    if (from != ps[0].Type)
                    {
                        if (op != null)
                        {
                            if (!ps[0].Type.IsAssignableFrom(from))
                            {
                                throw new ArgumentException(nameof(op));
                            }
                        }
                        else
                        {
                            EmitImplicitConversion(cg, from, ps[0].Type, @checked: @checked);
                        }
                    }
                    pconsumed++;
                }

                if (op != null)
                {
                    if (ps.Length > pconsumed)
                    {
                        EmitImplicitConversion(cg, op, ps[pconsumed].Type, @checked: @checked);
                    }
                    pconsumed++;
                }

                // Context ctx,
                if (ps.Length > pconsumed && SpecialParameterSymbol.IsContextParameter(ps[pconsumed]))
                {
                    cg.EmitLoadContext();
                    pconsumed++;
                }

                if (ps.Length != pconsumed)
                {
                    throw new InvalidOperationException();
                }

                EmitImplicitConversion(cg, cg.EmitCall(method.IsVirtual ? ILOpCode.Callvirt : ILOpCode.Call, method), to, @checked: true);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
Beispiel #2
0
        // resolve operator method
        public MethodSymbol ResolveOperator(TypeSymbol receiver, bool hasref, string[] opnames, TypeSymbol[] extensions, TypeSymbol operand = null, TypeSymbol target = null)
        {
            Debug.Assert(receiver != null);
            Debug.Assert(opnames != null && opnames.Length != 0);

            MethodSymbol candidate           = null;
            int          candidatecost       = int.MaxValue; // candidate cost
            int          candidatecost_minor = 0;            // second cost

            for (int ext = -1; ext < extensions.Length; ext++)
            {
                // TODO: go through interfaces

                for (var container = ext < 0 ? receiver : extensions[ext]; container != null; container = container.IsStatic ? null : container.BaseType)
                {
                    if (container.SpecialType == SpecialType.System_ValueType)
                    {
                        continue;                                                        //
                    }
                    for (int i = 0; i < opnames.Length; i++)
                    {
                        var members = container.GetMembers(opnames[i]);
                        for (int m = 0; m < members.Length; m++)
                        {
                            if (members[m] is MethodSymbol method)
                            {
                                if (ext >= 0 && !method.IsStatic)
                                {
                                    continue;                                  // only static methods allowed in extension containers
                                }
                                if (method.DeclaredAccessibility != Accessibility.Public)
                                {
                                    continue;
                                }
                                if (method.Arity != 0)
                                {
                                    continue;                    // CONSIDER
                                }
                                // TODO: replace with overload resolution

                                int cost       = 0;
                                int cost_minor = 0;

                                if (target != null && method.ReturnType != target)
                                {
                                    var conv = ClassifyConversion(method.ReturnType, target, ConversionKind.Numeric | ConversionKind.Reference);
                                    if (conv.Exists)    // TODO: chain the conversion, sum the cost
                                    {
                                        cost += ConvCost(conv, method.ReturnType, target);
                                    }
                                    else
                                    {
                                        continue;
                                    }
                                }

                                var ps        = method.Parameters;
                                int pconsumed = 0;

                                // TSource receiver,
                                if (method.IsStatic)
                                {
                                    if (ps.Length <= pconsumed)
                                    {
                                        continue;
                                    }
                                    bool isbyref = ps[pconsumed].RefKind != RefKind.None;
                                    if (isbyref && hasref == false)
                                    {
                                        continue;
                                    }
                                    // if (container != receiver && ps[pconsumed].HasThisAttribute == false) continue; // [ThisAttribute] // proper extension method
                                    var pstype = ps[pconsumed].Type;
                                    if (pstype != receiver)
                                    {
                                        if (isbyref)
                                        {
                                            continue;          // cannot convert addr
                                        }
                                        var conv = ClassifyConversion(receiver, pstype, ConversionKind.Numeric | ConversionKind.Reference);
                                        if (conv.Exists)   // TODO: chain the conversion
                                        {
                                            cost += ConvCost(conv, receiver, pstype);
                                        }
                                        else
                                        {
                                            continue;
                                        }
                                    }
                                    pconsumed++;
                                }

                                // Context ctx,
                                if (pconsumed < ps.Length && SpecialParameterSymbol.IsContextParameter(ps[pconsumed]))
                                {
                                    pconsumed++;
                                    cost_minor--; // specialized operator - prefered
                                }

                                // TOperand,
                                if (operand != null)
                                {
                                    if (ps.Length <= pconsumed)
                                    {
                                        continue;
                                    }
                                    if (ps[pconsumed].Type != operand)
                                    {
                                        var conv = ClassifyConversion(operand, ps[pconsumed].Type, ConversionKind.Implicit);
                                        if (conv.Exists)    // TODO: chain the conversion
                                        {
                                            cost += ConvCost(conv, operand, ps[pconsumed].Type);
                                        }
                                        else
                                        {
                                            continue;
                                        }
                                    }
                                    pconsumed++;
                                }

                                // Context ctx,
                                if (pconsumed < ps.Length && SpecialParameterSymbol.IsContextParameter(ps[pconsumed]))
                                {
                                    pconsumed++;
                                    cost_minor--;   // specialized operator - prefered
                                }

                                if (ps.Length != pconsumed)
                                {
                                    continue;
                                }

                                if (container.SpecialType == SpecialType.System_Object ||
                                    container.IsValueType)
                                {
                                    //cost++; // should be enabled
                                    cost_minor++;   // implicit conversion
                                }

                                //
                                if (cost < candidatecost || (cost == candidatecost && cost_minor < candidatecost_minor))
                                {
                                    candidate           = method;
                                    candidatecost       = cost;
                                    candidatecost_minor = cost_minor;
                                }
                            }
                        }
                    }
                }
            }

            //

            return(candidate);
        }
        /// <summary>
        /// Emits the given conversion. 'from' and 'to' matches the classified conversion.
        /// </summary>
        public static void EmitConversion(this CodeGenerator cg, CommonConversion conversion, TypeSymbol from, TypeSymbol to, TypeSymbol op = null, bool @checked = false)
        {
            // {from}, {op} is loaded on stack

            //

            if (conversion.Exists == false)
            {
                throw cg.NotImplementedException($"Conversion from '{from}' to '{to}' ");
            }

            if (conversion.IsNullable)
            {
                if (from.IsNullableType(out var ttype))
                {
                    if (op != null)
                    {
                        // TODO
                        throw new ArgumentException(nameof(op));
                    }

                    var lbltrue = new NamedLabel("has value");
                    var lblend  = new NamedLabel("end");
                    var tmp     = cg.GetTemporaryLocal(from, true);
                    cg.Builder.EmitLocalStore(tmp);

                    // Template: tmp.HasValue ? convert(tmp.Value) : default
                    cg.Builder.EmitLocalAddress(tmp);
                    cg.EmitCall(ILOpCode.Call, cg.DeclaringCompilation.System_Nullable_T_HasValue(from));
                    cg.Builder.EmitBranch(ILOpCode.Brtrue, lbltrue);

                    // default:
                    cg.EmitLoadDefault(to);
                    cg.Builder.EmitBranch(ILOpCode.Br, lblend);
                    // cg.Builder.AdjustStack(-1); // ?

                    // lbltrue:
                    cg.Builder.MarkLabel(lbltrue);
                    // Template: convert( tmp.GetValueOrDefault() )
                    cg.Builder.EmitLocalAddress(tmp);
                    cg.EmitCall(ILOpCode.Call, cg.DeclaringCompilation.System_Nullable_T_GetValueOrDefault(from)).Expect(ttype);
                    EmitConversion(cg, conversion.WithIsNullable(false), ttype, to, op, @checked);

                    // lblend:
                    cg.Builder.MarkLabel(lblend);

                    return;
                }

                if (to.IsNullableType(out ttype)) // NOTE: not used yet
                {
                    // new Nullable<TType>( convert(from) )
                    EmitConversion(cg, conversion.WithIsNullable(false), from, ttype, op, @checked);
                    cg.EmitCall(ILOpCode.Newobj, ((NamedTypeSymbol)to).InstanceConstructors[0]); // new Nullable<T>( STACK )
                    return;
                }

                throw Roslyn.Utilities.ExceptionUtilities.Unreachable;
            }

            if (conversion.IsIdentity)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                if (to.SpecialType == SpecialType.System_Void)
                {
                    // POP
                    cg.EmitPop(from);
                }

                // nop
            }
            else if (conversion.IsNumeric)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                EmitNumericConversion(cg, from, to, @checked: @checked);
            }
            else if (conversion.IsReference)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                // TODO: ensure from/to is a valid reference type
                cg.EmitCastClass(to);
            }
            else if (conversion.IsUserDefined)
            {
                var method    = (MethodSymbol)conversion.MethodSymbol;
                var ps        = method.Parameters;
                int pconsumed = 0;

                if (method.HasThis)
                {
                    if (from.IsValueType)
                    {
                        if (op != null || from.IsVoid())
                        {
                            throw new ArgumentException(nameof(op));
                        }

                        cg.EmitStructAddr(from);
                    }
                }
                else
                {
                    if (ps[0].RefKind != RefKind.None)
                    {
                        throw new InvalidOperationException();
                    }
                    if (from != ps[0].Type)
                    {
                        if (op != null)
                        {
                            if (!ps[0].Type.IsAssignableFrom(from))
                            {
                                throw new ArgumentException(nameof(op));
                            }
                        }
                        else
                        {
                            EmitImplicitConversion(cg, from, ps[0].Type, @checked: @checked);
                        }
                    }
                    pconsumed++;
                }

                if (op != null)
                {
                    if (ps.Length > pconsumed)
                    {
                        EmitImplicitConversion(cg, op, ps[pconsumed].Type, @checked: @checked);
                    }
                    pconsumed++;
                }

                // Context ctx,
                if (ps.Length > pconsumed && SpecialParameterSymbol.IsContextParameter(ps[pconsumed]))
                {
                    cg.EmitLoadContext();
                    pconsumed++;
                }

                if (ps.Length != pconsumed)
                {
                    throw new InvalidOperationException();
                }

                EmitImplicitConversion(cg, cg.EmitCall(method.IsVirtual ? ILOpCode.Callvirt : ILOpCode.Call, method), to, @checked: true);
            }
            else
            {
                throw new NotImplementedException();
            }
        }