/// <summary> /// Pres the process method invocation. /// </summary> /// <param name="expression">The expression.</param> /// <param name="methodName">Name of the method.</param> /// <param name="declarations">The declarations.</param> /// <returns></returns> protected virtual void ProcessMethodInvocation(MethodInvocationExpression expression, string methodName, List <IDeclaration> declarations) { // Get all arguments types infered var argumentTypeInferences = expression.Arguments.Select(x => x.TypeInference).ToArray(); var argumentTypes = expression.Arguments.Select(x => x.TypeInference.TargetType).ToArray(); // If any type could not be resolved previously, there is already an error, so return immediately if (argumentTypes.Any(x => x == null)) { return; } var overloads = new List <FunctionOverloadScore>(); // Use the most overriden method // TODO: Temporary workaround for user methods overriding builtin methods // Remove the builtin methods if there is any overriding var methodsDeclared = declarations.OfType <MethodDeclaration>().ToList(); for (int i = 0; i < methodsDeclared.Count - 1; i++) { var leftMethod = methodsDeclared[i]; for (int j = i + 1; j < methodsDeclared.Count; j++) { if (leftMethod.IsSameSignature(methodsDeclared[j])) { methodsDeclared.RemoveAt(i); i--; break; } } } // Try to match the function arguments with every overload foreach (var methodDeclaration in methodsDeclared) { var returnType = methodDeclaration.ReturnType.ResolveType(); var parameterTypes = methodDeclaration.Parameters.Select(x => x.Type.ResolveType()).ToArray(); // Number of parameters doesn't match if (argumentTypes.Length > parameterTypes.Length) { continue; } // Check for method calls that is using implicit parameter value if (argumentTypes.Length < parameterTypes.Length) { bool allRemainingParametersHaveDefaultValues = true; // Check for default values for (int i = argumentTypes.Length; i < parameterTypes.Length; i++) { if (methodDeclaration.Parameters[i].InitialValue == null) { allRemainingParametersHaveDefaultValues = false; break; } } // If remaining parameters doesn't have any default values, then continue if (!allRemainingParametersHaveDefaultValues) { continue; } } // Higher score = more conversion (score == 0 is perfect match) int score = 0; bool validOverload = true; // Check parameters for (int i = 0; i < argumentTypes.Length && validOverload; ++i) { var argType = argumentTypes[i]; var expectedType = parameterTypes[i]; var argTypeBase = TypeBase.GetBaseType(argType); var expectedTypeBase = TypeBase.GetBaseType(expectedType); if (expectedTypeBase is GenericParameterType) { var genericParameterType = (GenericParameterType)expectedTypeBase; // TODO handle dynamic score from constraint. if (methodDeclaration.CheckConstraint(genericParameterType, argType)) { score++; } else { validOverload = false; } } else { // TODO, improve the whole test by using TypeBase equality when possible // Then work on scalar type conversion ( float to int, signed to unsigned, different type) var fromScalarType = argTypeBase as ScalarType; var toScalarType = expectedTypeBase as ScalarType; if (fromScalarType != null && toScalarType != null) { if (ScalarType.IsFloat(fromScalarType) && !ScalarType.IsFloat(toScalarType)) { // Truncation from float to int score += 7; } else if (fromScalarType != toScalarType) { // else different type (implicit cast is usually working) score += 1; } if (!fromScalarType.IsUnsigned && toScalarType.IsUnsigned) { // int to unsigned score += 2; } } // First, try to fix the base type (i.e. the "float" of float3x1) if (argTypeBase != expectedTypeBase && expectedTypeBase is ScalarType) { if (!(argTypeBase is ScalarType)) { score++; // +1 for type conversion } argType = TypeBase.CreateWithBaseType(argType, (ScalarType)expectedTypeBase); } validOverload = TestMethodInvocationArgument(argTypeBase, expectedTypeBase, argType, expectedType, ref score); } } if (validOverload) { overloads.Add( new FunctionOverloadScore { Declaration = methodDeclaration, ParameterTypes = parameterTypes, ReturnType = returnType, Score = score }); } } // In-place sort using List.Sort would be lighter var bestOverload = overloads.OrderBy(x => x.Score).FirstOrDefault(); if (bestOverload != null) { expression.TypeInference.TargetType = bestOverload.ReturnType.ResolveType(); // Override declaration to match exactly the declaration found by method overloaded resolution expression.Target.TypeInference.Declaration = bestOverload.Declaration; // Add appropriate cast for (int i = 0; i < argumentTypes.Length; ++i) { argumentTypeInferences[i].ExpectedType = (bestOverload.ParameterTypes[i] is GenericParameterType) ? argumentTypes[i] : bestOverload.ParameterTypes[i].ResolveType(); } } else { Error(MessageCode.ErrorNoOverloadedMethod, expression.Span, methodName); } }