Ejemplo n.º 1
0
        /// <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);
            }
        }