Repository: numba/numba

Issue:
@stencil fails for return type of boolean
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

For more general "how do I do X?" type questions, please speak to us in real
time on https://gitter.im/numba/numba or post to the Numba mailing list
https://groups.google.com/a/continuum.io/forum/#!forum/numba-users.

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [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).

From https://groups.google.com/a/continuum.io/forum/#!topic/numba-users/ldhJdkTCdE0

This:
```python
from numba import stencil, njit
import numpy as np

@stencil
def get_boundary(mask):
    return (mask[-1, -1] ^ mask[-1, 0] ^ mask[-1, 1] ^
            mask[0, -1]  ^ mask[0, 0]  ^ mask[0, 1]  ^
            mask[1, -1]  ^ mask[1, 0]  ^ mask[1, 1])

A = np.array(np.arange(20) % 2).reshape(4, 5).astype(np.bool_)

def foo(array):
    return get_boundary(array)

print(njit(parallel=True)(foo)(A))
print(njit(foo)(A))
```
does:
```
$ python ml1.py 
[[False False False False False]
 [False False  True False False]
 [False  True False  True False]
 [False False False False False]]
Traceback (most recent call last):
  File "ml1.py", line 16, in <module>
    print(njit(foo)(A))
  File "<path>/numba/numba/dispatcher.py", line 348, in _compile_for_args
    error_rewrite(e, 'typing')
  File "<path>/numba/numba/dispatcher.py", line 315, in error_rewrite
    reraise(type(e), e, None)
  File "<path>/numba/numba/six.py", line 658, in reraise
    raise value.with_traceback(tb)
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython mode backend)
Failed in nopython mode pipeline (step: nopython frontend)
Invalid use of Function(<built-in function zeros>) with argument(s) of type(s): (tuple(int64 x 2), dtype=Function(<class 'bool'>))
 * parameterized
In definition 0:
    All templates rejected
This error is usually caused by passing an argument of a type that is unsupported by the named function.
[1] During: resolving callee type: Function(<built-in function zeros>)
[2] During: typing of call at <string> (3)


File "<string>", line 3:
<source missing, REPL/exec in use?>

[1] During: lowering "$0.3 = call $0.1(array, func=$0.1, args=[Var(array, ml1.py (13))], kws=(), vararg=None)" at ml1.py (13)
```
issue is that in the string generated for the stencil kernel `np.{}` is used, and `bool` dtype evaluates as a string to `np.bool` which is the function when `bool_` is meant.



Discussion:

Resolving patch:
diff --git a/numba/stencil.py b/numba/stencil.py
--- a/numba/stencil.py
+++ b/numba/stencil.py
@@ -529,18 +529,18 @@ def _stencil_wrapper(self, result, sigret, return_type, typemap, calltypes, *arg
         # then us numpy.full if the user specified a cval stencil decorator option
         # or np.zeros if they didn't to allocate the array.
         if result is None:
+            return_type_name = numpy_support.as_dtype(
+                               return_type.dtype).type.__name__
             if "cval" in self.options:
                 cval = self.options["cval"]
                 if return_type.dtype != typing.typeof.typeof(cval):
                     raise ValueError(
                         "cval type does not match stencil return type.")
-
                 out_init ="{} = np.full({}, {}, dtype=np.{})\n".format(
-                            out_name, shape_name, cval, return_type.dtype)
-
+                            out_name, shape_name, cval, return_type_name)
             else:
                 out_init ="{} = np.zeros({}, dtype=np.{})\n".format(
-                            out_name, shape_name, return_type.dtype)
+                            out_name, shape_name, return_type_name)
             func_text += "    " + out_init
 
         offset = 1


Test patch:
diff --git a/numba/tests/test_stencils.py b/numba/tests/test_stencils.py
--- a/numba/tests/test_stencils.py
+++ b/numba/tests/test_stencils.py
@@ -659,6 +659,9 @@ def gen_num(self, value):
             """
             Generates an ast.Num of value `value`
             """
+            # pretend bools are ints, ast has no boolean literal support
+            if isinstance(value, bool):
+                return ast.Num(int(value))
             if abs(value) >= 0:
                 return ast.Num(value)
             else:
@@ -2490,6 +2493,25 @@ def kernel(a):
         a = np.arange(10. * 20.).reshape(10, 20)
         self.check(kernel, a)
 
+    def test_basic92(self):
+        """ Issue #3497, bool return type evaluating incorrectly. """
+        def kernel(a):
+            return (a[-1, -1] ^ a[-1, 0] ^ a[-1, 1] ^
+                    a[0, -1]  ^ a[0, 0]  ^ a[0, 1]  ^
+                    a[1, -1]  ^ a[1, 0]  ^ a[1, 1])
+
+        A = np.array(np.arange(20) % 2).reshape(4, 5).astype(np.bool_)
+        self.check(kernel, A)
+
+    def test_basic93(self):
+        """ Issue #3497, bool return type evaluating incorrectly. """
+        def kernel(a):
+            return (a[-1, -1] ^ a[-1, 0] ^ a[-1, 1] ^
+                    a[0, -1]  ^ a[0, 0]  ^ a[0, 1]  ^
+                    a[1, -1]  ^ a[1, 0]  ^ a[1, 1])
+
+        A = np.array(np.arange(20) % 2).reshape(4, 5).astype(np.bool_)
+        self.check(kernel, A, options={'cval': True})
 
 if __name__ == "__main__":
     unittest.main()