Repository: numba/numba

Issue:
Mismatching `@jit` signature falls back on object mode
This is a bit surprising and confusing:

```
>>> def f(x, y): return x+y
... 
>>> jit(int32(int32,))(f)
CPUOverloaded(<function f at 0x7fafa3c3fbf8>)
```

Here, the signature is obviously wrong (one argument type is given for a two-arguments function), but Numba just goes ahead and falls back on object mode. It would be more helpful to raise an error.



Discussion:
Apparently this error is allowed in object mode... test_blackscholes relies on it. Do we want to support it?

I'm assuming that blacksholes relies on this behavior on accident due to a bug in the test as well.  We should fix the bug and raise a proper error when the signature doesn't match.


Resolving patch:
diff --git a/numba/compiler.py b/numba/compiler.py
--- a/numba/compiler.py
+++ b/numba/compiler.py
@@ -7,7 +7,7 @@
 
 from numba import (bytecode, interpreter, typing, typeinfer, lowering,
                    objmode, irpasses, utils, config, type_annotations,
-                   types, ir, assume, looplifting, macro)
+                   types, ir, assume, looplifting, macro, types)
 from numba.targets import cpu
 
 
@@ -306,8 +306,14 @@ def stage_analyze_bytecode(self):
         """
         self.interp = translate_stage(self.bc)
         self.nargs = len(self.interp.argspec.args)
-        if len(self.args) > self.nargs:
-            raise TypeError("Too many argument types")
+        if not self.args and self.flags.force_pyobject:
+            # Allow an empty argument types specification when object mode
+            # is explicitly requested.
+            self.args = (types.pyobject,) * self.nargs
+        elif len(self.args) != self.nargs:
+            raise TypeError("Signature mismatch: %d argument types given, "
+                            "but function takes %d arguments"
+                            % (len(self.args), self.nargs))
 
     def frontend_looplift(self):
         """


Test patch:
diff --git a/numba/tests/test_blackscholes.py b/numba/tests/test_blackscholes.py
--- a/numba/tests/test_blackscholes.py
+++ b/numba/tests/test_blackscholes.py
@@ -127,10 +127,13 @@ def test_array_expr(self):
         flags.set("enable_pyobject")
 
         global cnd_array_jitted
-        cr1 = compile_isolated(cnd_array, args=(), flags=flags)
+        scalty = types.float64
+        arrty = types.Array(scalty, 1, 'C')
+        cr1 = compile_isolated(cnd_array, args=(arrty,), flags=flags)
         cnd_array_jitted = cr1.entry_point
-        cr2 = compile_isolated(blackscholes_arrayexpr_jitted, args=(),
-                                     flags=flags)
+        cr2 = compile_isolated(blackscholes_arrayexpr_jitted,
+                               args=(arrty, arrty, arrty, scalty, scalty),
+                               flags=flags)
         jitted_bs = cr2.entry_point
 
         OPT_N = 400
diff --git a/numba/tests/test_dispatcher.py b/numba/tests/test_dispatcher.py
--- a/numba/tests/test_dispatcher.py
+++ b/numba/tests/test_dispatcher.py
@@ -130,6 +130,23 @@ def check(*args, **kwargs):
             f(3, 4, y=6)
         self.assertIn("missing argument 'z'", str(cm.exception))
 
+    def test_signature_mismatch(self):
+        tmpl = "Signature mismatch: %d argument types given, but function takes 2 arguments"
+        with self.assertRaises(TypeError) as cm:
+            jit("()")(add)
+        self.assertIn(tmpl % 0, str(cm.exception))
+        with self.assertRaises(TypeError) as cm:
+            jit("(intc,)")(add)
+        self.assertIn(tmpl % 1, str(cm.exception))
+        with self.assertRaises(TypeError) as cm:
+            jit("(intc,intc,intc)")(add)
+        self.assertIn(tmpl % 3, str(cm.exception))
+        # With forceobj=True, an empty tuple is accepted
+        jit("()", forceobj=True)(add)
+        with self.assertRaises(TypeError) as cm:
+            jit("(intc,)", forceobj=True)(add)
+        self.assertIn(tmpl % 1, str(cm.exception))
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/numba/tests/test_storeslice.py b/numba/tests/test_storeslice.py
--- a/numba/tests/test_storeslice.py
+++ b/numba/tests/test_storeslice.py
@@ -9,7 +9,7 @@ def setitem_slice(a, start, stop, step, scalar):
     a[start:stop:step] = scalar
 
 
-def usecase(obs, nPoints, B, sigB, A, sigA, M, sigM):
+def usecase(obs, nPoints):
     center = nPoints / 2
     obs[0:center] = np.arange(center)
     obs[center] = 321
@@ -24,9 +24,10 @@ def test_usecase(self):
 
         flags = Flags()
         flags.set("enable_pyobject")
-        cres = compile_isolated(usecase, (), flags=flags)
-        cres.entry_point(obs_got, n, 10.0, 1.0, 2.0, 3.0, 4.0, 5.0)
-        usecase(obs_expected, n, 10.0, 1.0, 2.0, 3.0, 4.0, 5.0)
+        cres = compile_isolated(usecase, (types.float64[:], types.intp),
+                                flags=flags)
+        cres.entry_point(obs_got, n)
+        usecase(obs_expected, n)
 
         self.assertTrue(np.allclose(obs_got, obs_expected))
 
diff --git a/numba/tests/test_unpack_sequence.py b/numba/tests/test_unpack_sequence.py
--- a/numba/tests/test_unpack_sequence.py
+++ b/numba/tests/test_unpack_sequence.py
@@ -77,7 +77,7 @@ class TestUnpack(TestCase):
 
     def test_unpack_list(self):
         pyfunc = unpack_list
-        cr = compile_isolated(pyfunc, (), flags=enable_pyobj_flags)
+        cr = compile_isolated(pyfunc, (), flags=force_pyobj_flags)
         cfunc = cr.entry_point
         l = [1, 2, 3]
         self.assertEqual(cfunc(l), pyfunc(l))
diff --git a/numba/tests/test_usecases.py b/numba/tests/test_usecases.py
--- a/numba/tests/test_usecases.py
+++ b/numba/tests/test_usecases.py
@@ -167,7 +167,7 @@ def test_string_len(self):
 
     def test_string_slicing(self):
         pyfunc = usecases.string_slicing
-        cr = compile_isolated(pyfunc, (types.pyobject,),
+        cr = compile_isolated(pyfunc, (types.pyobject,) * 3,
                               flags=enable_pyobj_flags)
         cfunc = cr.entry_point
 
@@ -192,7 +192,7 @@ def test_string_conversion(self):
     def test_string_comparisons(self):
         import operator
         pyfunc = usecases.string_comparison
-        cr = compile_isolated(pyfunc, (types.pyobject, types.pyobject),
+        cr = compile_isolated(pyfunc, (types.pyobject, types.pyobject, types.pyobject),
                               flags=enable_pyobj_flags)
         cfunc = cr.entry_point