Repository: numba/numba

Issue:
`AssertionError` if you don't pass a `Signature` to `cuda.declare_device`
The following:

```python
from numba import cuda, int32

sig = (int32[:], int32)
b_fwd = cuda.declare_device('b', sig)

def a(q, x):
    b_fwd(q, x-1)

cuda.compile_ptx_for_current_device(a, sig, device=True)
```

results in:

```
...
  File "/home/gmarkall/numbadev/numba/numba/core/typed_passes.py", line 105, in run_pass
    typemap, return_type, calltypes, errs = type_inference_stage(
  File "/home/gmarkall/numbadev/numba/numba/core/typed_passes.py", line 83, in type_inference_stage
    errs = infer.propagate(raise_errors=raise_errors)
  File "/home/gmarkall/numbadev/numba/numba/core/typeinfer.py", line 1078, in propagate
    errors = self.constraints.propagate(self)
  File "/home/gmarkall/numbadev/numba/numba/core/typeinfer.py", line 177, in propagate
    raise e
  File "/home/gmarkall/numbadev/numba/numba/core/typeinfer.py", line 155, in propagate
    constraint(typeinfer)
  File "/home/gmarkall/numbadev/numba/numba/core/typeinfer.py", line 578, in __call__
    self.resolve(typeinfer, typevars, fnty)
  File "/home/gmarkall/numbadev/numba/numba/core/typeinfer.py", line 632, in resolve
    typeinfer.add_type(self.target, sig.return_type, loc=self.loc)
  File "/home/gmarkall/numbadev/numba/numba/core/typeinfer.py", line 1098, in add_type
    unified = tv.add_type(tp, loc=loc)
  File "/home/gmarkall/numbadev/numba/numba/core/typeinfer.py", line 55, in add_type
    assert isinstance(tp, types.Type), type(tp)
```

I think this is because `declare_device` expects a `Signature` object but doesn't check what it receives.


Discussion:
(noticed when working on a workaround for #7697, cc @c200chromebook)

Resolving patch:
diff --git a/numba/cuda/decorators.py b/numba/cuda/decorators.py
--- a/numba/cuda/decorators.py
+++ b/numba/cuda/decorators.py
@@ -129,4 +129,8 @@ def autojitwrapper(func):
 
 def declare_device(name, sig):
     argtypes, restype = sigutils.normalize_signature(sig)
+    if restype is None:
+        msg = 'Return type must be provided for device declarations'
+        raise TypeError(msg)
+
     return declare_device_function(name, restype, argtypes)


Test patch:
diff --git a/numba/cuda/tests/cudapy/test_device_func.py b/numba/cuda/tests/cudapy/test_device_func.py
--- a/numba/cuda/tests/cudapy/test_device_func.py
+++ b/numba/cuda/tests/cudapy/test_device_func.py
@@ -4,7 +4,7 @@
 import numpy as np
 
 from numba.cuda.testing import unittest, skip_on_cudasim, CUDATestCase
-from numba import cuda, jit, int32
+from numba import cuda, jit, float32, int32
 from numba.core.errors import TypingError
 
 
@@ -192,6 +192,31 @@ def rgba_caller(x, channels):
 
         self.assertEqual(0x04010203, x[0])
 
+    def _test_declare_device(self, decl):
+        self.assertEqual(decl.name, 'f1')
+        self.assertEqual(decl.sig.args, (float32[:],))
+        self.assertEqual(decl.sig.return_type, int32)
+
+    @skip_on_cudasim('cudasim does not check signatures')
+    def test_declare_device_signature(self):
+        f1 = cuda.declare_device('f1', int32(float32[:]))
+        self._test_declare_device(f1)
+
+    @skip_on_cudasim('cudasim does not check signatures')
+    def test_declare_device_string(self):
+        f1 = cuda.declare_device('f1', 'int32(float32[:])')
+        self._test_declare_device(f1)
+
+    @skip_on_cudasim('cudasim does not check signatures')
+    def test_bad_declare_device_tuple(self):
+        with self.assertRaisesRegex(TypeError, 'Return type'):
+            cuda.declare_device('f1', (float32[:],))
+
+    @skip_on_cudasim('cudasim does not check signatures')
+    def test_bad_declare_device_string(self):
+        with self.assertRaisesRegex(TypeError, 'Return type'):
+            cuda.declare_device('f1', '(float32[:],)')
+
 
 if __name__ == '__main__':
     unittest.main()