267 lines
8.4 KiB
Python
267 lines
8.4 KiB
Python
import numpy as np
|
|
|
|
from numba.core.compiler import compile_isolated, DEFAULT_FLAGS
|
|
from numba.cuda.testing import SerialMixin
|
|
from numba import typeof, cuda, njit
|
|
from numba.core.types import float64
|
|
from numba.tests.support import MemoryLeakMixin, override_env_config
|
|
from numba.core import config
|
|
import unittest
|
|
|
|
BOUNDSCHECK_FLAGS = DEFAULT_FLAGS.copy()
|
|
BOUNDSCHECK_FLAGS.boundscheck = True
|
|
|
|
|
|
def basic_array_access(a):
|
|
return a[10]
|
|
|
|
|
|
def slice_array_access(a):
|
|
# The first index (slice) is not bounds checked
|
|
return a[10:, 10]
|
|
|
|
|
|
def fancy_array_access(x):
|
|
a = np.array([1, 2, 3])
|
|
return x[a]
|
|
|
|
|
|
class TestBoundsCheckNoError(MemoryLeakMixin, unittest.TestCase):
|
|
def setUp(self):
|
|
self.old_boundscheck = config.BOUNDSCHECK
|
|
config.BOUNDSCHECK = None
|
|
|
|
def test_basic_array_boundscheck(self):
|
|
a = np.arange(5)
|
|
# Check the numpy behavior to make sure the test is correct
|
|
with self.assertRaises(IndexError):
|
|
# TODO: When we raise the same error message as numpy, test that
|
|
# they are the same
|
|
basic_array_access(a)
|
|
|
|
at = typeof(a)
|
|
c_noboundscheck = compile_isolated(basic_array_access, [at],
|
|
flags=DEFAULT_FLAGS)
|
|
noboundscheck = c_noboundscheck.entry_point
|
|
# Check that the default flag doesn't raise
|
|
noboundscheck(a)
|
|
# boundscheck(a) is tested in TestBoundsCheckError below
|
|
|
|
def test_slice_array_boundscheck(self):
|
|
a = np.ones((5, 5))
|
|
b = np.ones((5, 20))
|
|
with self.assertRaises(IndexError):
|
|
# TODO: When we raise the same error message as numpy, test that
|
|
# they are the same
|
|
slice_array_access(a)
|
|
# Out of bounds on a slice doesn't raise
|
|
slice_array_access(b)
|
|
|
|
at = typeof(a)
|
|
rt = float64[:]
|
|
c_noboundscheck = compile_isolated(slice_array_access, [at],
|
|
return_type=rt,
|
|
flags=DEFAULT_FLAGS)
|
|
noboundscheck = c_noboundscheck.entry_point
|
|
c_boundscheck = compile_isolated(slice_array_access, [at],
|
|
return_type=rt,
|
|
flags=BOUNDSCHECK_FLAGS)
|
|
boundscheck = c_boundscheck.entry_point
|
|
# Check that the default flag doesn't raise
|
|
noboundscheck(a)
|
|
noboundscheck(b)
|
|
# boundscheck(a) is tested in TestBoundsCheckError below
|
|
|
|
# Doesn't raise
|
|
boundscheck(b)
|
|
|
|
def test_fancy_indexing_boundscheck(self):
|
|
a = np.arange(3)
|
|
b = np.arange(4)
|
|
|
|
# Check the numpy behavior to ensure the test is correct.
|
|
with self.assertRaises(IndexError):
|
|
# TODO: When we raise the same error message as numpy, test that
|
|
# they are the same
|
|
fancy_array_access(a)
|
|
fancy_array_access(b)
|
|
|
|
at = typeof(a)
|
|
rt = at.dtype[:]
|
|
c_noboundscheck = compile_isolated(fancy_array_access, [at],
|
|
return_type=rt,
|
|
flags=DEFAULT_FLAGS)
|
|
noboundscheck = c_noboundscheck.entry_point
|
|
c_boundscheck = compile_isolated(fancy_array_access, [at],
|
|
return_type=rt,
|
|
flags=BOUNDSCHECK_FLAGS)
|
|
boundscheck = c_boundscheck.entry_point
|
|
# Check that the default flag doesn't raise
|
|
noboundscheck(a)
|
|
noboundscheck(b)
|
|
# boundscheck(a) is tested in TestBoundsCheckError below
|
|
|
|
# Doesn't raise
|
|
boundscheck(b)
|
|
|
|
def tearDown(self):
|
|
config.BOUNDSCHECK = self.old_boundscheck
|
|
|
|
|
|
class TestNoCudaBoundsCheck(SerialMixin, unittest.TestCase):
|
|
def setUp(self):
|
|
self.old_boundscheck = config.BOUNDSCHECK
|
|
config.BOUNDSCHECK = None
|
|
|
|
@unittest.skipIf(not cuda.is_available(), "NO CUDA")
|
|
def test_no_cuda_boundscheck(self):
|
|
with self.assertRaises(NotImplementedError):
|
|
@cuda.jit(boundscheck=True)
|
|
def func():
|
|
pass
|
|
|
|
# Make sure we aren't raising "not supported" error if we aren't
|
|
# requesting bounds checking anyway. Related pull request: #5257
|
|
@cuda.jit(boundscheck=False)
|
|
def func3():
|
|
pass
|
|
|
|
with override_env_config('NUMBA_BOUNDSCHECK', '1'):
|
|
@cuda.jit
|
|
def func2(x, a):
|
|
a[1] = x[1]
|
|
|
|
a = np.ones((1,))
|
|
x = np.zeros((1,))
|
|
# Out of bounds but doesn't raise (it does raise in the simulator,
|
|
# so skip there)
|
|
if not config.ENABLE_CUDASIM:
|
|
func2[1, 1](x, a)
|
|
|
|
def tearDown(self):
|
|
config.BOUNDSCHECK = self.old_boundscheck
|
|
|
|
|
|
# This is a separate test because the jitted functions that raise exceptions
|
|
# have memory leaks.
|
|
class TestBoundsCheckError(unittest.TestCase):
|
|
def setUp(self):
|
|
self.old_boundscheck = config.BOUNDSCHECK
|
|
config.BOUNDSCHECK = None
|
|
|
|
def test_basic_array_boundscheck(self):
|
|
a = np.arange(5)
|
|
# Check the numpy behavior to make sure the test is correct
|
|
with self.assertRaises(IndexError):
|
|
# TODO: When we raise the same error message as numpy, test that
|
|
# they are the same
|
|
basic_array_access(a)
|
|
|
|
at = typeof(a)
|
|
c_boundscheck = compile_isolated(basic_array_access, [at],
|
|
flags=BOUNDSCHECK_FLAGS)
|
|
boundscheck = c_boundscheck.entry_point
|
|
|
|
with self.assertRaises(IndexError):
|
|
boundscheck(a)
|
|
|
|
def test_slice_array_boundscheck(self):
|
|
a = np.ones((5, 5))
|
|
b = np.ones((5, 20))
|
|
with self.assertRaises(IndexError):
|
|
# TODO: When we raise the same error message as numpy, test that
|
|
# they are the same
|
|
slice_array_access(a)
|
|
# Out of bounds on a slice doesn't raise
|
|
slice_array_access(b)
|
|
|
|
at = typeof(a)
|
|
rt = float64[:]
|
|
c_boundscheck = compile_isolated(slice_array_access, [at],
|
|
return_type=rt,
|
|
flags=BOUNDSCHECK_FLAGS)
|
|
boundscheck = c_boundscheck.entry_point
|
|
with self.assertRaises(IndexError):
|
|
boundscheck(a)
|
|
|
|
def test_fancy_indexing_boundscheck(self):
|
|
a = np.arange(3)
|
|
b = np.arange(4)
|
|
|
|
# Check the numpy behavior to ensure the test is correct.
|
|
with self.assertRaises(IndexError):
|
|
# TODO: When we raise the same error message as numpy, test that
|
|
# they are the same
|
|
fancy_array_access(a)
|
|
fancy_array_access(b)
|
|
|
|
at = typeof(a)
|
|
rt = at.dtype[:]
|
|
c_boundscheck = compile_isolated(fancy_array_access, [at],
|
|
return_type=rt,
|
|
flags=BOUNDSCHECK_FLAGS)
|
|
boundscheck = c_boundscheck.entry_point
|
|
with self.assertRaises(IndexError):
|
|
boundscheck(a)
|
|
|
|
def tearDown(self):
|
|
config.BOUNDSCHECK = self.old_boundscheck
|
|
|
|
|
|
class TestBoundsEnvironmentVariable(unittest.TestCase):
|
|
def setUp(self):
|
|
self.old_boundscheck = config.BOUNDSCHECK
|
|
config.BOUNDSCHECK = None
|
|
|
|
@njit
|
|
def default(x):
|
|
return x[1]
|
|
|
|
@njit(boundscheck=False)
|
|
def off(x):
|
|
return x[1]
|
|
|
|
@njit(boundscheck=True)
|
|
def on(x):
|
|
return x[1]
|
|
|
|
self.default = default
|
|
self.off = off
|
|
self.on = on
|
|
|
|
def test_boundscheck_unset(self):
|
|
with override_env_config('NUMBA_BOUNDSCHECK', ''):
|
|
a = np.array([1])
|
|
|
|
# Doesn't raise
|
|
self.default(a)
|
|
self.off(a)
|
|
|
|
with self.assertRaises(IndexError):
|
|
self.on(a)
|
|
|
|
def test_boundscheck_enabled(self):
|
|
with override_env_config('NUMBA_BOUNDSCHECK', '1'):
|
|
a = np.array([1])
|
|
|
|
with self.assertRaises(IndexError):
|
|
self.default(a)
|
|
self.off(a)
|
|
self.on(a)
|
|
|
|
def test_boundscheck_disabled(self):
|
|
with override_env_config('NUMBA_BOUNDSCHECK', '0'):
|
|
a = np.array([1])
|
|
|
|
# Doesn't raise
|
|
self.default(a)
|
|
self.off(a)
|
|
self.on(a)
|
|
|
|
def tearDown(self):
|
|
config.BOUNDSCHECK = self.old_boundscheck
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|