Repository: numba/numba

Issue:
named parameters not recognized with first class functions
- [x ] I am using the latest ~~released~~ **master** version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x ] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).


There is a problem recognizing named parameters. The following fails

```python
from numba import njit, types

@njit(types.int64(types.int64))
def foo(x):
    return x+1

@njit
def loop(foos):
    f = foos[0]
    return f(x=1)

loop((foo,))

TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Internal error at <numba.core.typeinfer.CallConstraint object at 0x7f67f39a1a30>.
mismatch of arguments number: 0 vs 1
[1] During: resolving callee type: UndefinedFunctionType[undefined(undefined)20]
```

while this works

```python
@njit
def loop(foos):
    f = foos[0]
    return f(1)

loop((foo,))
```


Discussion:
I'm presently on this commit:

```
commit ce2139c7dd93127efb04b35f28f4bebc7f44dfd5 (HEAD -> master, numba/master, numba/HEAD)
Merge: 6e3272769 70944d381
Author: Siu Kwan Lam <michael.lam.sk@gmail.com>
Date:   Thu Apr 9 11:10:15 2020 -0500

    Merge pull request #5538 from stuartarchibald/wip/change_log_049rc2
    
    Update CHANGE_LOG for 0.49.0rc2
```

and the output of the reproducer is:

```
$ python repro.py 
/home/gmarkall/numbadev/numba/numba/core/utils.py:509: NumbaExperimentalFeatureWarning: First-class function type feature is experimental
  category=NumbaExperimentalFeatureWarning)
repro.py:9: NumbaExperimentalFeatureWarning: First-class function type feature is experimental
  f = foos[0]
```

Is this fixed already? Or do you see the same problem with a very recent master, @luk-f-a ?
@gmarkall , you're right it does not happen on the latest master! I was on something between rc1 and now, but not today's master. Sorry for the noise!
@gmarkall , now I realized what happened. The last commit in master disables first-class functions in many cases (including the reproducer). So the error does not appear, but not for being fixed in the first class function implementation, but for not using first-class functions.
I'm changing the reproducer above, so it fails again on the latest master.
CC @pearu, please could you take a look at this? Thanks.
I can reproduce the issue and I am afraid that calling first-class functions with named parameters cannot be supported.

Consider the following example
```
@njit('int64(int64)')
def foo1(x):  # never used here
    return x + 1

@njit('int64(int64)')
def foo2(y):
    return y + 1

@njit
def bar(funcs):
    f = funcs[0]
    return f(x = 1)

bar((foo2,))
```
There is no clear way to relate the `y` argument of `foo2` to the named parameter `x`.
Notice that the above example would not make sense in pure Python either.

A possible solution would be to attach parameter names to function signatures, for instance:
```
@njit('int64(int64:x)')
def foo2(y):
    return y + 1
```
but even then the mapping of `x` to `y` is obscure, imho.

In conclusion, first-class functions must be called using positional arguments without argument names.
For now, i think we should fix it with better error message.

Resolving patch:
diff --git a/numba/core/types/function_type.py b/numba/core/types/function_type.py
--- a/numba/core/types/function_type.py
+++ b/numba/core/types/function_type.py
@@ -4,7 +4,7 @@
 
 from abc import ABC, abstractmethod
 from .abstract import Type
-from .. import types
+from .. import types, errors
 
 
 class FunctionType(Type):
@@ -43,6 +43,17 @@ def dump(self, tab=''):
     def get_call_type(self, context, args, kws):
         from numba.core import typing
 
+        if kws:
+            # First-class functions carry only the type signature
+            # information and function address value. So, it is not
+            # possible to determine the positional arguments
+            # corresponding to the keyword arguments in the call
+            # expression. For instance, the definition of the
+            # first-class function may not use the same argument names
+            # that the caller assumes. [numba/issues/5540].
+            raise errors.UnsupportedError(
+                f'first-class function call cannot use keyword arguments')
+
         if len(args) != self.nargs:
             raise ValueError(
                 f'mismatch of arguments number: {len(args)} vs {self.nargs}')


Test patch:
diff --git a/numba/tests/test_function_type.py b/numba/tests/test_function_type.py
--- a/numba/tests/test_function_type.py
+++ b/numba/tests/test_function_type.py
@@ -1028,3 +1028,28 @@ def bar():
             return bar_scalar(*formulae_foo)
 
         self.assertEqual(bar(), 30)
+
+    def test_issue_5540(self):
+
+        @njit(types.int64(types.int64))
+        def foo(x):
+            return x + 1
+
+        @njit
+        def bar_bad(foos):
+            f = foos[0]
+            return f(x=1)
+
+        @njit
+        def bar_good(foos):
+            f = foos[0]
+            return f(1)
+
+        self.assertEqual(bar_good((foo, )), 2)
+
+        with self.assertRaises(errors.TypingError) as cm:
+            bar_bad((foo, ))
+
+        self.assertRegex(
+            str(cm.exception),
+            r'.*first-class function call cannot use keyword arguments')