MethodSymbol CreateGhostOverload(PEModuleBuilder module, DiagnosticBag diagnostic, int pcount)
 {
     Debug.Assert(this.Parameters.Length > pcount);
     return(GhostMethodBuilder.CreateGhostOverload(
                this, this.ContainingType, module, diagnostic,
                ghostreturn: this.ReturnType, ghostparams: this.Parameters.Take(pcount), explicitOverride: null));
 }
        /// <summary>
        /// Synthesizes method overloads in case there are optional parameters which explicit default value cannot be resolved as a <see cref="ConstantValue"/>.
        /// </summary>
        /// <remarks>
        /// foo($a = [], $b = [1, 2, 3])
        /// + foo() => foo([], [1, 2, 3)
        /// + foo($a) => foo($a, [1, 2, 3])
        /// </remarks>
        protected IList <MethodSymbol> SynthesizeOverloadsWithOptionalParameters(PEModuleBuilder module, DiagnosticBag diagnostic)
        {
            List <MethodSymbol> list = null;

            var ps = this.Parameters;

            for (int i = 0; i < ps.Length; i++)
            {
                if (ps[i] is IPhpValue p && p.Initializer != null && ps[i].ExplicitDefaultConstantValue == null)   // => ConstantValue couldn't be resolved for optional parameter
                {
                    if (list == null)
                    {
                        list = new List <MethodSymbol>();
                    }

                    if (this.ContainingType.IsInterface)
                    {
                        // TODO: we can't build instance method in an interface
                        // - generate static extension method ?
                        // - annotate parameter with attribute and the initializer value?
                        //   ? [Optional(EmptyArray)]
                        //   ? [Optional(array(1,2,3))]
                        Debug.WriteLine($"we've lost parameter explicit default value {this.ContainingType.Name}::{this.RoutineName}, parameter ${ps[i].Name}");
                    }
                    else
                    {
                        // create ghost stub foo(p0, .. pi-1) => foo(p0, .. , pN)
                        list.Add(GhostMethodBuilder.CreateGhostOverload(this, module, diagnostic, i));
                    }
                }
            }

            return(list ?? (IList <MethodSymbol>)Array.Empty <MethodSymbol>());
        }
        void EmitTraitImplementations(Emit.PEModuleBuilder module)
        {
            foreach (var t in TraitUses)
            {
                foreach (var m in t.GetMembers().OfType <SynthesizedMethodSymbol>())
                {
                    Debug.Assert(m.ForwardedCall != null);

                    module.SetMethodBody(m, MethodGenerator.GenerateMethodBody(module, m, il =>
                    {
                        IPlace thisPlace          = null;
                        IPlace traitInstancePlace = null;
                        IPlace ctxPlace;

                        if (m.IsStatic)
                        {
                            // Template: return TRAIT.method(...)
                            Debug.Assert(SpecialParameterSymbol.IsContextParameter(m.Parameters[0]));
                            ctxPlace = new ParamPlace(m.Parameters[0]);
                        }
                        else
                        {
                            // Template: return this.<>trait.method(...)
                            thisPlace          = new ArgPlace(this, 0);                                   // this
                            ctxPlace           = new FieldPlace(thisPlace, this.ContextStore, module);    // this.<ctx>
                            traitInstancePlace = new FieldPlace(thisPlace, t.TraitInstanceField, module); // this.<>trait
                        }

                        using (var cg = new CodeGenerator(il, module, DiagnosticBag.GetInstance(), module.Compilation.Options.OptimizationLevel, false, this, ctxPlace, thisPlace)
                        {
                            CallerType = this,
                        })
                        {
                            var forwarded_type = cg.EmitForwardCall(m.ForwardedCall, m, thisPlaceExplicit: traitInstancePlace);
                            var target_type    = m.ReturnType;

                            cg.EmitConvert(forwarded_type, 0, target_type); // always (forwarded_type === target_type)
                            cg.EmitRet(target_type);
                        }
                    }, null, DiagnosticBag.GetInstance(), false));
                    module.SynthesizedManager.AddMethod(this, m);

                    // ghost stubs: // TODO: resolve this already in SourceTypeSymbol.GetMembers(), now it does not get overloaded properly
                    var ps = m.Parameters;
                    for (int i = 0; i < ps.Length; i++)
                    {
                        if (ps[i].HasUnmappedDefaultValue)  // => ConstantValue couldn't be resolved for optional parameter
                        {
                            // create ghost stub foo(p0, .. pi-1) => foo(p0, .. , pN)
                            GhostMethodBuilder.CreateGhostOverload(m, module, DiagnosticBag.GetInstance(), i);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Synthesizes method overloads in case there are optional parameters which explicit default value cannot be resolved as a <see cref="ConstantValue"/>.
        /// </summary>
        /// <remarks>
        /// foo($a = [], $b = [1, 2, 3])
        /// + foo() => foo([], [1, 2, 3)
        /// + foo($a) => foo($a, [1, 2, 3])
        /// </remarks>
        protected IList <MethodSymbol> SynthesizeOverloadsWithOptionalParameters(PEModuleBuilder module, DiagnosticBag diagnostic)
        {
            List <MethodSymbol> list = null;

            var implparams      = ImplicitParameters;
            var srcparams       = SourceParameters;
            var implicitVarArgs = VarargsParam;

            for (int i = 0; i <= srcparams.Length; i++)                                                                                               // how many to be copied from {srcparams}
            {
                var isfake     = /*srcparams[i - 1].IsFake*/ implicitVarArgs != null && i > 0 && srcparams[i - 1].Ordinal >= implicitVarArgs.Ordinal; // parameter was replaced with [params]
                var hasdefault = i < srcparams.Length && srcparams[i].HasUnmappedDefaultValue;                                                        // ConstantValue couldn't be resolved for optional parameter

                if (isfake || hasdefault)
                {
                    if (this.ContainingType.IsInterface)
                    {
                        // TODO: we can't build instance method in an interface
                        // - generate static extension method ?
                        // - annotate parameter with attribute and the initializer value?
                        //   ? [Optional(EmptyArray)]
                        //   ? [Optional(array(1,2,3))]
                        if (i < srcparams.Length) // always true
                        {
                            Debug.WriteLine($"we've lost parameter explicit default value {this.ContainingType.Name}::{this.RoutineName}, parameter ${srcparams[i].Name}");
                        }
                    }
                    else
                    {
                        // ghostparams := [...implparams, ...srcparams{0..i-1}]
                        var ghostparams = new ParameterSymbol[implparams.Length + i];
                        implparams.CopyTo(ghostparams);
                        Array.Copy(srcparams, 0, ghostparams, implparams.Length, i);

                        // create ghost stub foo(p0, .. pi-1) => foo(p0, .. , pN)
                        var ghost = GhostMethodBuilder.CreateGhostOverload(
                            this, ContainingType, module, diagnostic,
                            ReturnType, ghostparams);

                        //
                        if (list == null)
                        {
                            list = new List <MethodSymbol>();
                        }

                        list.Add(ghost);
                    }
                }
            }

            return(list ?? (IList <MethodSymbol>)Array.Empty <MethodSymbol>());
        }
        /// <summary>
        /// Synthesizes method overloads in case there are optional parameters which explicit default value cannot be resolved as a <see cref="ConstantValue"/>.
        /// </summary>
        /// <remarks>
        /// foo($a = [], $b = [1, 2, 3])
        /// + foo() => foo([], [1, 2, 3)
        /// + foo($a) => foo($a, [1, 2, 3])
        /// </remarks>
        protected IList <MethodSymbol> SynthesizeOverloadsWithOptionalParameters(PEModuleBuilder module, DiagnosticBag diagnostic)
        {
            List <MethodSymbol> list = null;

            var implparams      = ImplicitParameters;
            var srcparams       = SourceParameters;
            var implicitVarArgs = VarargsParam;

            for (int i = 0; i <= srcparams.Length; i++)                                                                                               // how many to be copied from {srcparams}
            {
                var isfake     = /*srcparams[i - 1].IsFake*/ implicitVarArgs != null && i > 0 && srcparams[i - 1].Ordinal >= implicitVarArgs.Ordinal; // parameter was replaced with [params]
                var hasdefault = false;                                                                                                               // i < srcparams.Length && srcparams[i].HasUnmappedDefaultValue();  // ConstantValue couldn't be resolved for optional parameter

                if (isfake || hasdefault)
                {
                    if (this.ContainingType.IsInterface)
                    {
                        // we can't build instance method in an interface
                        // CONSIDER: generate static extension method ?
                    }
                    else
                    {
                        // ghostparams := [...implparams, ...srcparams{0..i-1}]
                        var ghostparams = ImmutableArray.CreateBuilder <ParameterSymbol>(implparams.Length + i);
                        ghostparams.AddRange(implparams);
                        ghostparams.AddRange(srcparams, i);

                        // create ghost stub foo(p0, .. pi-1) => foo(p0, .. , pN)
                        var ghost = GhostMethodBuilder.CreateGhostOverload(
                            this, ContainingType, module, diagnostic,
                            ReturnType, ghostparams.MoveToImmutable());

                        //
                        if (list == null)
                        {
                            list = new List <MethodSymbol>();
                        }

                        list.Add(ghost);
                    }
                }
            }

            return(list ?? (IList <MethodSymbol>)Array.Empty <MethodSymbol>());
        }
 void CreateGhostOverload(PEModuleBuilder module, DiagnosticBag diagnostic, int pcount)
 {
     Debug.Assert(this.Parameters.Length > pcount);
     GhostMethodBuilder.CreateGhostOverload(this, this.ContainingType, module, diagnostic, this.ReturnType, this.Parameters.Take(pcount), null);
 }