compiler: Map host numpy.bool_ values to TBool

Since we don't implement any integer-like operations for TBool
(addition, bitwise not, etc.), TBool is currently neither
strictly equivalent to builtin bool nor numpy.bool_, but through
very obvious compiler errors (operation not supported) rather than
silently different runtime behaviour.

Just mapping both to TBool thus is a huge improvement over the
current behaviour (where numpy.False_ is a true-like object). In
the future, we could still implement more operations for TBool,
presumably following numpy.bool_ rather than the builtin type,
just like builtin integers get translated to the numpy-like
TInt{32,64}.

GitHub: Fixes #1275.
This commit is contained in:
David Nadlinger 2021-03-20 00:41:52 +00:00 committed by Sebastien Bourdeauducq
parent 01352236ee
commit 9e3b6faceb
3 changed files with 32 additions and 4 deletions

View File

@ -162,10 +162,11 @@ class ASTSynthesizer:
typ = builtins.TNone()
return asttyped.NameConstantT(value=value, type=typ,
loc=self._add(repr(value)))
elif value is True or value is False:
elif isinstance(value, (bool, numpy.bool_)):
typ = builtins.TBool()
return asttyped.NameConstantT(value=value, type=typ,
loc=self._add(repr(value)))
coerced = bool(value)
return asttyped.NameConstantT(value=coerced, type=typ,
loc=self._add(repr(coerced)))
elif value is numpy.float:
typ = builtins.fn_float()
return asttyped.NameConstantT(value=None, type=typ,

View File

@ -1558,7 +1558,8 @@ class LLVMIRGenerator:
return ll.Constant.literal_struct([])
elif builtins.is_bool(typ):
assert value in (True, False), fail_msg
return ll.Constant(llty, value)
# Explicitly cast to bool to handle numpy.bool_.
return ll.Constant(llty, bool(value))
elif builtins.is_int(typ):
assert isinstance(value, (int, numpy.int32, numpy.int64)), fail_msg
return ll.Constant(llty, int(value))

View File

@ -36,6 +36,12 @@ class RoundtripTest(ExperimentCase):
self.assertRoundtrip(True)
self.assertRoundtrip(False)
def test_numpy_bool(self):
# These won't return as numpy.bool_, but the bare-Python results should still
# compare equal.
self.assertRoundtrip(numpy.True_)
self.assertRoundtrip(numpy.False_)
def test_int(self):
self.assertRoundtrip(numpy.int32(42))
self.assertRoundtrip(numpy.int64(42))
@ -492,3 +498,23 @@ class AssertTest(ExperimentCase):
check_fail(lambda: exp.check(False), "AssertionError")
exp.check_msg(True)
check_fail(lambda: exp.check_msg(False), "foo")
class _NumpyBool(EnvExperiment):
def build(self):
self.setattr_device("core")
self.np_true = numpy.True_
self.np_false = numpy.False_
@kernel
def run(self):
assert self.np_true
assert self.np_true == True
assert not self.np_false
assert self.np_false == False
class NumpyBoolTest(ExperimentCase):
def test_numpy_bool(self):
"""Test NumPy bools decay to ARTIQ compiler builtin bools as expected"""
self.create(_NumpyBool).run()