internal override void Bind(Binder binder) { // We have a series of temporary variables, so we will need // to discard them after the call is made binder.Bookmark(); // We have the process // Eval arg i, assign variable i, eval arg i+1, assign variable i+1 // so if we, for instance, bind every argument, then bind every temp // variable, we are sure to clobber our own arguments for (int i = 0; i < Arguments.Length; i++) { Arguments[i].Bind(binder); binder.BindVariable(ArgumentVariables[i]); } binder.Checkout(); bool fullyResolvedTypes; if (Callee == null) { fullyResolvedTypes = FunctionName.GetKnownType() != null; } else { fullyResolvedTypes = Callee.GetKnownType() != null; } RedwoodType[] argumentTypes = new RedwoodType[Arguments.Length]; for (int i = 0; i < Arguments.Length; i++) { argumentTypes[i] = Arguments[i].GetKnownType(); // Not really sure whether we care about this, but it might be useful // for compiling variable assignments ArgumentVariables[i].KnownType = argumentTypes[i]; fullyResolvedTypes &= argumentTypes[i] != null; } // If we have fully resolved types (ie. all argument types are known // and so is the callee type) we definitely know the return type FullyResolved = fullyResolvedTypes; // We have two options for determining an exact type, either: // 1) The method is fully resolved. One lambda type will come out of // the many, // or // 2) the method is not fully resolved, but the provided arguments // narrow the methods down a single option. if (Callee == null) { LambdaType = FunctionName.GetKnownType(); // If we have a lambda group, we should try to resolve it ResolveLambdaGroupOverload(argumentTypes); } else if (Callee.GetKnownType() == null) { LambdaType = null; } else if (Callee.GetKnownType().CSharpType == null) { LambdaType = Callee .GetKnownType() .GetKnownTypeOfMember(FunctionName.Name); // Make sure that calls to class functions also have the correct // lambda type ResolveLambdaGroupOverload(argumentTypes); } else if (Callee.GetKnownType().CSharpType == typeof(RedwoodType)) { if (!Callee.Constant) { LambdaType = null; } else if (Callee.EvaluateConstant() is RedwoodType calleeType) { if (calleeType.CSharpType == null) { LambdaType = calleeType .GetKnownTypeForStaticMember(FunctionName.Name); ResolveLambdaGroupOverload(argumentTypes); } else { MethodInfo[] infos; bool methodExists = MemberResolver.TryResolveMethod( null, calleeType, FunctionName.Name, true, out infos); if (!methodExists) { throw new NotImplementedException(); } BindExternal(infos); } } else { // This should never happen? throw new NotImplementedException(); } } else { MethodInfo[] infos; bool methodExists = MemberResolver.TryResolveMethod( null, Callee.GetKnownType(), FunctionName.Name, false, out infos); if (!methodExists) { // TODO: Can an object have a lambda field? throw new NotImplementedException(); } BindExternal(infos); } void BindExternal(MethodInfo[] infos) { ExternalMethodGroup = new MethodGroup(infos); ExternalMethodGroup.SelectOverloads(argumentTypes); if (ExternalMethodGroup.infos.Length == 0) { throw new NotImplementedException(); } else if (ExternalMethodGroup.infos.Length == 1) { RedwoodType returnType = RedwoodType.GetForCSharpType(ExternalMethodGroup.infos[0].ReturnType); RedwoodType[] paramTypes = ExternalMethodGroup.infos[0].GetParameters() .Select(param => RedwoodType.GetForCSharpType(param.ParameterType)) .ToArray(); LambdaType = RedwoodType.GetForLambdaArgsTypes( typeof(ExternalLambda), returnType, paramTypes); } } }