protected MethodResolutionException(MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceEngine engine, int? mismatchIndex, Exception innerException)
            : base(innerException)
        {
            OriginalSignature = originalSignature;
            InferredSignature = inferredSignature;
            RootExpression = engine.Root;
            MismatchIndex = mismatchIndex;

            try
            {
                // ensure that all call args are inferred successfully
                foreach (var arg in engine.Root.CallArgs())
                {
                    // we need this check because depending on the context
                    // call args might already be inferred by the moment of crash
                    if (!engine.Inferences.ContainsKey(arg))
                    {
                        // this is unlikely to crash and ruin our attempt to provide the client with comprehensive info
                        // since type inference for arguments has already been probed before entering TypeInferenceMethods.Resolve
                        engine.InferTypes(arg);
                    }
                }

                ActualArguments = engine.Ctx.Root.ToXArgs(engine.Inferences).ToArray();
                FormalArguments = (InferredSignature ?? OriginalSignature).ToXArgs(ActualArguments.Length).ToArray();

                // the lines below are copy&pasted from GenericArgsInference.cs
                for (var i = 0; i < ActualArguments.Length; ++i)
                {
                    var ai_r = ActualArguments.ElementAt(i);
                    var pi = FormalArguments.ElementAt(i);

                    if (!pi.IsOpenGeneric())
                    {
                        if (pi.IsFunctionType() && ai_r is Lambda)
                        {
                            var ai_s = ((Lambda)ai_r).Type.GetFunctionSignature();
                            var pi_s = pi.GetFunctionSignature();

                            if (ai_s.GetParameters().Length == pi_s.GetParameters().Length)
                            {
                                var callArg = engine.Ctx.Root.CallArgs().ElementAt(i - 1);
                                ActualArguments[i] = new Lambda(
                                    (LambdaExpression)callArg,
                                    (pi_s.GetParameters()
                                        .Select(pi2 => pi2.ParameterType.IsGenericParameter ? 
                                            typeof(Variant) : pi2.ParameterType)
                                        .Concat(typeof(Variant).AsArray()))
                                        .ForgeFuncType());
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
                // sigh, the client won't get full info from the crash site
                // sad, but true, so just ignore possible exceptions
            }
        }
        private void InferMethodGroup(MethodGroup mg, RelinqScriptExpression root, TypeInferenceCache cache)
        {
            // q: should existing but not accessible (security!) methods be included into the resolution?
            // a: yes and here's why:
            // scenario 1. Included; when overload resolution binds to an unauthorized method = crash.
            // scenario 2. Not included, so overload resolution binds to an unexpected method = fail.

            // lets us the fail fast if the resolution is unnecessary.
            // inferences made here won't be wasted anyways since they get cached
            var ctx = new TypeInferenceContext(root, cache, Integration);
            var preview = new TypeInferenceEngine(ctx);
            root.CallArgs().ForEach(child => preview.InferTypes(child));

            // we cannot pass the preview.Ctx inside since it might have
            // potentially half-inferred lambdas that won't be able to be reinferred
            // by MG resolution since ctx is init only.
            var resolved = mg.Resolve(ctx);

            cache.Add(root, resolved.Signature.ReturnType);
            cache.Invocations.Add(root, resolved);
            cache.Upgrade(resolved.Inferences);
        }
 public Expression Compile(String relinqScriptCode)
 {
     var ast = new RelinqScriptParser(relinqScriptCode, Integration).Parse();
     var inferences = new TypeInferenceEngine(ast, Integration).InferTypes();
     return new CSharpExpressionTreeBuilder(ast, Integration).Compile(inferences);
 }
 public GenericArgumentsInferenceException(GenericArgumentsInferenceExceptionType type, MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceEngine engine, int? mismatchIndex, Exception innerException)
     : base(originalSignature, inferredSignature, engine, mismatchIndex, innerException)
 {
     Type = type;
 }
 public GenericArgumentsInferenceException(GenericArgumentsInferenceExceptionType type, MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceEngine engine, Exception innerException)
     : this(type, originalSignature, inferredSignature, engine, null, innerException)
 {
     
 }
        public GenericArgumentsInferenceException(GenericArgumentsInferenceExceptionType type, MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceEngine engine, int? mismatchIndex)
            : this(type, originalSignature, inferredSignature, engine, mismatchIndex, null)
        {

        }
        public static MethodInfo InferGenericTypeParams(this MethodInfo mi, TypeInferenceContext ctx)
        {
            var originalSignature = mi;
            TypeInferenceEngine preview = null;

            try
            {
                preview = new TypeInferenceEngine(ctx);
                ctx.Root.CallArgs().ForEach(child => preview.InferTypes(child));

                var fail = Array.FindIndex(ctx.Root.CallArgs().ToArray(), c => preview.Inferences[c] is Variant);
                if (fail != -1)
                {
                    throw new GenericArgumentsInferenceException(
                        GenericArgumentsInferenceExceptionType.VariantArg,
                        originalSignature, mi, preview, fail);
                }
                else
                {
                    Func<IEnumerable<RelinqScriptType>> args = () => ctx.Root.ToXArgs(preview.Inferences);
                    Func<IEnumerable<Type>> @params = () => mi.ToXArgs(args().Count());

                    var argc = args().Count();
                    if (argc != @params().Count())
                    {
                        throw new GenericArgumentsInferenceException(
                            GenericArgumentsInferenceExceptionType.ArgcMismatch,
                            originalSignature, mi, preview);
                    }
                    else
                    {
                        var processed = new TrackableList<bool>(Enumerable.Repeat(false, argc));
                        var fresh = new List<bool>(Enumerable.Repeat(true, argc));
                        processed.ListChanged += (o, e) => fresh[e.Index] = true;

                        while (fresh.Any(b => b) && !processed.All(b => b))
                        {
                            Enumerable.Range(0, argc).ForEach(i => fresh[i] = false);
                            foreach (var i in processed.ToArray()
                                .Select((b, j) => new { b, j }).Where(_ => !_.b).Select(_ => _.j))
                            {
                                var ai_r = args().ElementAt(i);
                                var pi = @params().ElementAt(i);

                                var argIndex = mi.IsExtension() ? i : i - 1;

                                if (!pi.IsOpenGeneric())
                                {
                                    if (pi.IsFunctionType() && ai_r is Lambda)
                                    {
                                        var ai_s = ((Lambda)ai_r).Type.GetFunctionSignature();
                                        var pi_s = pi.GetFunctionSignature();

                                        // todo. support varargs here as well
                                        if (ai_s.GetParameters().Length == pi_s.GetParameters().Length)
                                        {
                                            var preview2 = new TypeInferenceEngine(ctx);

                                            // todo. the line below is only possible because we don't process lambda extension calls
                                            var callArg = ctx.Root.CallArgs().ElementAt(i - 1);
                                            preview2.Ctx.Inferences.Add(callArg, new Lambda(
                                                (LambdaExpression)callArg,
                                                (pi_s.GetParameters()
                                                    .Select(pi2 => pi2.ParameterType.IsGenericParameter ?
                                                        typeof(Variant) : pi2.ParameterType)
                                                    .Concat(typeof(Variant).AsArray()))
                                                    .ForgeFuncType()));

                                            try
                                            {
                                                preview2.InferTypes(callArg);
                                            }
                                            catch (RelinqException rex)
                                            {
                                                if (rex.IsUnexpected)
                                                {
                                                    throw;
                                                }
                                                else
                                                {
                                                    throw new GenericArgumentsInferenceException(
                                                        GenericArgumentsInferenceExceptionType.LambdaInconsistentBody,
                                                        originalSignature, mi, preview, i, rex);
                                                }
                                            }
                                            catch (Exception ex)
                                            {
                                                throw new GenericArgumentsInferenceException(
                                                    GenericArgumentsInferenceExceptionType.Unexpected,
                                                    originalSignature, mi, preview, i, ex);
                                            }

                                            var lambdaBody = ((LambdaExpression)callArg).Body;
                                            var retval = preview2.Inferences[lambdaBody];

                                            if (retval is ClrType)
                                            {
                                                var ai_ret = ((ClrType)retval).Type;
                                                var pi_ret = pi_s.ReturnType;

                                                if (!ai_ret.HasImplicitCastTo(pi_ret))
                                                {
                                                    throw new GenericArgumentsInferenceException(
                                                        GenericArgumentsInferenceExceptionType.LambdaIncompatibleRetval,
                                                        originalSignature, mi, preview, i);
                                                }
                                            }
                                            else
                                            {
                                                // lambdas that return lambdas or method groups
                                                // are not implemented, since they're not supported by C# spec.
                                                throw new GenericArgumentsInferenceException(
                                                    GenericArgumentsInferenceExceptionType.LambdaInvalidRetval,
                                                    originalSignature, mi, preview, i);
                                            }
                                        }
                                        else
                                        {
                                            throw new GenericArgumentsInferenceException(
                                                GenericArgumentsInferenceExceptionType.LambdaArgcMismatch,
                                                originalSignature, mi, preview, i);
                                        }
                                    }

                                    processed[i] = true;
                                }
                                else
                                {
                                    if (ai_r is ClrType)
                                    {
                                        var ai = ((ClrType)ai_r).Type;

                                        // todo. also check implicit casts here?
                                        var inf = ai.LookupInheritanceChain()
                                            .Select(aij => pi.InferFrom(aij))
                                            .FirstOrDefault(inf2 => inf2 != null);

                                        if (inf != null)
                                        {
                                            mi = mi.InferStructuralUnits(inf.ToDictionary(
                                                kvp => "a" + argIndex + kvp.Key,
                                                kvp => kvp.Value));

                                            if (mi != null)
                                            {
                                                processed[i] = true;
                                            }
                                            else
                                            {
                                                throw new GenericArgumentsInferenceException(
                                                    GenericArgumentsInferenceExceptionType.InferencesContradictGenericConstraints,
                                                    originalSignature, mi, preview, i);
                                            }
                                        }
                                        else
                                        {
                                            throw new GenericArgumentsInferenceException(
                                                GenericArgumentsInferenceExceptionType.FormalArgCannotBeInferredFromActualArg,
                                                originalSignature, mi, preview, i);
                                        }
                                    }
                                    else if (ai_r is Lambda)
                                    {
                                        if (pi.IsFunctionType())
                                        {
                                            var ai_s = ((Lambda)ai_r).Type.GetFunctionSignature();
                                            var pi_s = pi.GetFunctionSignature();

                                            // todo. support varargs here as well
                                            if (ai_s.GetParameters().Length == pi_s.GetParameters().Length)
                                            {
                                                var preview2 = new TypeInferenceEngine(ctx);

                                                // todo. the line below is only possible because we don't process lambda extension calls
                                                var callArg = ctx.Root.CallArgs().ElementAt(i - 1);
                                                preview2.Ctx.Inferences.Add(callArg, new Lambda(
                                                    (LambdaExpression)callArg,
                                                    (pi_s.GetParameters()
                                                        .Select(pi2 => pi2.ParameterType.IsGenericParameter ?
                                                            typeof(Variant) : pi2.ParameterType)
                                                        .Concat(typeof(Variant).AsArray()))
                                                        .ForgeFuncType()));

                                                try
                                                {
                                                    preview2.InferTypes(callArg);
                                                }
                                                catch (RelinqException rex)
                                                {
                                                    if (rex.IsUnexpected)
                                                    {
                                                        throw;
                                                    }
                                                    else
                                                    {
                                                        throw new GenericArgumentsInferenceException(
                                                            GenericArgumentsInferenceExceptionType.LambdaInconsistentBody,
                                                            originalSignature, mi, preview, i, rex);
                                                    }
                                                }
                                                catch (Exception ex)
                                                {
                                                    throw new GenericArgumentsInferenceException(
                                                        GenericArgumentsInferenceExceptionType.Unexpected,
                                                        originalSignature, mi, preview, i, ex);
                                                }

                                                var lambdaBody = ((LambdaExpression)callArg).Body;
                                                var retval = preview2.Inferences[lambdaBody];

                                                if (retval is ClrType)
                                                {
                                                    var ai_ret = ((ClrType)retval).Type;
                                                    var pi_ret = pi_s.ReturnType;

                                                    if (pi_ret.IsOpenGeneric())
                                                    {
                                                        var inf = ai_ret.LookupInheritanceChain()
                                                            .Select(ai_retj => pi_ret.InferFrom(ai_retj))
                                                            .FirstOrDefault(inf2 => inf2 != null);

                                                        if (inf != null)
                                                        {
                                                            var map = pi.GetStructuralTree();
                                                            mi = mi.InferStructuralUnits(inf.ToDictionary(
                                                                kvp => "a" + argIndex + map.Single(kvp2 =>
                                                                    kvp2.Value == pi_ret.SelectStructuralUnit(kvp.Key)).Key,
                                                                kvp => kvp.Value));

                                                            if (mi != null)
                                                            {
                                                                processed[i] = true;
                                                            }
                                                            else
                                                            {
                                                                throw new GenericArgumentsInferenceException(
                                                                    GenericArgumentsInferenceExceptionType.InferencesContradictGenericConstraints,
                                                                    originalSignature, mi, preview, i);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            throw new GenericArgumentsInferenceException(
                                                                GenericArgumentsInferenceExceptionType.FormalArgCannotBeInferredFromActualArg,
                                                                originalSignature, mi, preview, i);
                                                        }
                                                    }
                                                    else
                                                    {
                                                        processed[i] = true;
                                                    }
                                                }
                                                else if (retval is Variant)
                                                {
                                                    continue;
                                                }
                                                // lambdas that return lambdas or method groups
                                                // are not implemented, since they're not supported by C# spec.
                                                else
                                                {
                                                    throw new GenericArgumentsInferenceException(
                                                        GenericArgumentsInferenceExceptionType.LambdaInvalidRetval,
                                                        originalSignature, mi, preview, i);
                                                }
                                            }
                                            else
                                            {
                                                throw new GenericArgumentsInferenceException(
                                                    GenericArgumentsInferenceExceptionType.LambdaArgcMismatch,
                                                    originalSignature, mi, preview, i);
                                            }
                                        }
                                        else
                                        {
                                            throw new GenericArgumentsInferenceException(
                                                GenericArgumentsInferenceExceptionType.FormalArgCannotBeInferredFromActualArg,
                                                originalSignature, mi, preview, i);
                                        }
                                    }
                                    // unlike lambdas, method groups don't participate in type args inference
                                    // neither they can help to infer type args,
                                    // nor can they be inferred themselves.
                                    else
                                    {
                                        processed[i] = true;
                                    }
                                }
                            }
                        }

                        if (!mi.IsOpenGeneric())
                        {
                            return mi;
                        }
                        else
                        {
                            throw new GenericArgumentsInferenceException(
                                GenericArgumentsInferenceExceptionType.IncompleteInferenceSet,
                                originalSignature, mi, preview);
                        }
                    }
                }
            }
            catch (GenericArgumentsInferenceException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new GenericArgumentsInferenceException(
                    GenericArgumentsInferenceExceptionType.Unexpected,
                    originalSignature, mi, preview ?? new TypeInferenceEngine(ctx), ex);
            }
        }
        // todo. resolve the method group -> delegate cast
        // and also provide generic arg inference altogether
        public static ResolvedInvocation Resolve(this MethodGroup mg, TypeInferenceContext ctx)
        {
            var failboats = new Dictionary<MethodInfo, MethodResolutionException>();
            var infToOrig = new Dictionary<MethodInfo, MethodInfo>();
            var origToInf = new Dictionary<MethodInfo, MethodInfo>();

            try
            {
                // Step 1. Get rid of alternatives that don't have a form that suits arg count.
                var suitableAlts = new List<MethodInfo>();
                foreach (var alt in mg.Alts)
                {
                    if (alt.XArgsCount().Contains(ctx.Root.XArgsCount()))
                    {
                        suitableAlts.Add(alt);
                    }
                    else
                    {
                        failboats.Add(alt, new MethodMatchingException(
                            MethodMatchingExceptionType.ArgcMismatch, alt, null, ctx));
                    }
                }

                // Step 2. Infer generic arguments of all open generic alternatives and get rid of those who fail to be inferred.
                var inferredAlts = new List<MethodInfo>();
                foreach (var salt in suitableAlts)
                {
                    try
                    {
                        var infAlt = salt.InferGenericTypeParams(ctx);

                        inferredAlts.Add(infAlt);
                        origToInf.Add(salt, infAlt);
                        infToOrig.Add(infAlt, salt);
                    }
                    catch (GenericArgumentsInferenceException gex)
                    {
                        if (gex.IsUnexpected)
                        {
                            throw;
                        }
                        else
                        {
                            failboats.Add(salt, new MethodMatchingException(
                                MethodMatchingExceptionType.GenericArgInferenceFailed, salt, null, ctx, gex));
                        }
                    }
                }

                // Step 3. Store type inference context of all alternatives that had made it so far
                var sigs = new List<ResolvedSignature>();
                foreach (var infAlt in inferredAlts)
                {
                    var infAlt1 = infAlt;
                    var resolvedCtx = new TypeInferenceEngine(ctx);

                    ctx.Root.CallArgs().ForEach((callArg, i) =>
                    {
                        if (callArg is LambdaExpression)
                        {
                            var inferredFormal = infAlt1.IsExtension()
                                ? infAlt1.GetParameters()[i + 1].ParameterType
                                : infAlt1.GetParameters()[i].ParameterType;

                            var inferredLambda = new Lambda(
                                (LambdaExpression)callArg, inferredFormal.NormalizeFunctionType());
                            resolvedCtx.Ctx.Inferences.Add(callArg, inferredLambda);
                        }

                        resolvedCtx.InferTypes(callArg);
                    });

                    sigs.Add(new ResolvedSignature(resolvedCtx.Ctx, infAlt));
                }

                // Step 4. Now get rid of alternatives that do not match Relinq-specific validation
                var validSigs = new List<ResolvedSignature>();
                foreach (var sig in sigs)
                {
                    var args = ctx.Root.ToXArgs(sig.Inferences);
                    if (sig.Signature.IsValid(sig.Signature.IsExtension() ? args : args.Skip(1)))
                    {
                        validSigs.Add(sig);
                    }
                    else
                    {
                        failboats.Add(infToOrig[sig.Signature], new MethodMatchingException(
                            MethodMatchingExceptionType.SignatureFailsMetaConstrains, infToOrig[sig.Signature], sig.Signature, ctx));
                    }
                }

                // Step 5. Eliminate signatures that do not satisfy actual argument conversions
                var invs = new List<ResolvedInvocation>();
                foreach (var validSig in validSigs)
                {
                    var args = ctx.Root.ToXArgs(validSig.Inferences);
                    var @params = validSig.Signature.ToXArgs(ctx.Root.XArgsCount());
                    var inv = new ResolvedInvocation(validSig, args.Zip(@params, (arg, param) => Cast.Lookup(arg, param)));

                    var fail = Array.FindIndex(inv.Casts.ToArray(), c => c == null);
                    if (fail == -1)
                    {
                        invs.Add(inv);
                    }
                    else
                    {
                        failboats.Add(infToOrig[validSig.Signature], new MethodMatchingException(
                            MethodMatchingExceptionType.ActualArgCannotBeCastToFormalArg, infToOrig[validSig.Signature], validSig.Signature, ctx, fail));
                    }
                }

                // Step 6. Remove sub-par signatures from the all-satisfying survivors
                var res = new List<ResolvedInvocation>();
                foreach (var inv in invs)
                {
                    var subpar = false;
                    foreach (var anotherInv in invs)
                    {
                        subpar |= anotherInv > inv;
                    }

                    if (!subpar)
                    {
                        res.Add(inv);
                    }
                    else
                    {
                        failboats.Add(infToOrig[inv.Signature], new MethodMatchingException(
                            MethodMatchingExceptionType.SignatureIsSubPar, infToOrig[inv.Signature], inv.Signature, ctx));
                    }
                }

                // Step 7. Finalize the death race (and evade the final trap)
                switch (res.Count())
                {
                    case 0:
                        throw new MethodGroupResolutionFailedException(
                            JSToCSharpExceptionType.FruitlessMethodGroupResolution, mg, failboats, ctx);

                    case 1:
                        var winrar = res.Single();
                        if (winrar.Signature.IsTrap())
                        {
                            failboats.Add(infToOrig[winrar.Signature], new MethodMatchingException(
                                MethodMatchingExceptionType.SignatureIsEntrapped, infToOrig[winrar.Signature], winrar.Signature, ctx));

                            throw new MethodGroupResolutionFailedException(
                                JSToCSharpExceptionType.FruitlessMethodGroupResolution, mg, failboats, ctx);
                        }

                        // my sincere congratulations
                        return winrar;

                    default:
                        foreach (var winrar2 in res)
                        {
                            failboats.Add(infToOrig[winrar2.Signature], new MethodMatchingException(
                                MethodMatchingExceptionType.SignatureIsOkButAmbiguous, infToOrig[winrar2.Signature], winrar2.Signature, ctx));
                        }

                        throw new MethodGroupResolutionFailedException(
                            JSToCSharpExceptionType.AmbiguousMethodGroupResolution, mg, failboats, ctx);
                }
            }
            catch (MethodGroupResolutionFailedException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new MethodGroupResolutionFailedException(
                    JSToCSharpExceptionType.Unexpected, mg, failboats, ctx, ex);
            }
        }
 protected MethodResolutionException(MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceEngine engine, Exception innerException)
     : this(originalSignature, inferredSignature, engine, null, innerException)
 {
     
 }
        protected MethodResolutionException(MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceEngine engine, int? mismatchIndex)
            : this(originalSignature, inferredSignature, engine, mismatchIndex, null)
        {

        }