presto-main/src/main/java/io/prestosql/metadata/PolymorphicScalarFunction.java/PolymorphicScalarFunction/matchesParameterAndReturnTypes
class PolymorphicScalarFunction:    private static boolean matchesParameterAndReturnTypes(
            MethodAndNativeContainerTypes methodAndNativeContainerTypes,
            List<Type> resolvedTypes,
            Type returnType,
            List<ArgumentProperty> argumentProperties,
            boolean nullableResult)
    {
        Method method = methodAndNativeContainerTypes.getMethod();
        checkState(method.getParameterCount() >= resolvedTypes.size(),
                "method %s has not enough arguments: %s (should have at least %s)", method.getName(), method.getParameterCount(), resolvedTypes.size());

        Class<?>[] methodParameterJavaTypes = method.getParameterTypes();
        for (int i = 0, methodParameterIndex = 0; i < resolvedTypes.size(); i++) {
            NullConvention nullConvention = argumentProperties.get(i).getNullConvention();
            Class<?> expectedType = null;
            Class<?> actualType;
            switch (nullConvention) {
                case RETURN_NULL_ON_NULL:
                case USE_NULL_FLAG:
                    expectedType = methodParameterJavaTypes[methodParameterIndex];
                    actualType = getNullAwareContainerType(resolvedTypes.get(i).getJavaType(), false);
                    break;
                case USE_BOXED_TYPE:
                    expectedType = methodParameterJavaTypes[methodParameterIndex];
                    actualType = getNullAwareContainerType(resolvedTypes.get(i).getJavaType(), true);
                    break;
                case BLOCK_AND_POSITION:
                    Optional<Class<?>> explicitNativeContainerTypes = methodAndNativeContainerTypes.getExplicitNativeContainerTypes().get(i);
                    if (explicitNativeContainerTypes.isPresent()) {
                        expectedType = explicitNativeContainerTypes.get();
                    }
                    actualType = getNullAwareContainerType(resolvedTypes.get(i).getJavaType(), false);
                    break;
                default:
                    throw new UnsupportedOperationException("unknown NullConvention");
            }
            if (!actualType.equals(expectedType)) {
                return false;
            }
            methodParameterIndex += nullConvention.getParameterCount();
        }
        return method.getReturnType().equals(getNullAwareContainerType(returnType.getJavaType(), nullableResult));
    }