From e2fd819742a1d9934f051079a48c8a4faece439f Mon Sep 17 00:00:00 2001 From: Wanda Date: Tue, 13 Feb 2024 05:31:51 +0100 Subject: [PATCH] hdl._ast: fix `shift_right` and `as_signed` edge cases. --- amaranth/hdl/_ast.py | 13 +++++++++++-- tests/test_hdl_ast.py | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/amaranth/hdl/_ast.py b/amaranth/hdl/_ast.py index 6355654..f8860e8 100644 --- a/amaranth/hdl/_ast.py +++ b/amaranth/hdl/_ast.py @@ -483,7 +483,14 @@ class Value(metaclass=ABCMeta): Returns ------- :class:`Value`, :pc:`signed(len(self))`, :ref:`assignable ` + + Raises + ------ + ValueError + If :pc:`len(self) == 0`. """ + if len(self) == 0: + raise ValueError("Cannot create a 0-width signed value") return Operator("s", [self]) def __bool__(self): @@ -900,9 +907,9 @@ class Value(metaclass=ABCMeta): Returns ------- - :class:`Value`, :pc:`unsigned(len(self) + amount)` + :class:`Value`, :pc:`unsigned(max(len(self) + amount, 0))` If :pc:`self` is unsigned. - :class:`Value`, :pc:`signed(len(self) + amount)` + :class:`Value`, :pc:`signed(max(len(self) + amount, 1))` If :pc:`self` is signed. """ if not isinstance(amount, int): @@ -974,6 +981,8 @@ class Value(metaclass=ABCMeta): if amount < 0: return self.shift_left(-amount) if self.shape().signed: + if amount >= len(self): + amount = len(self) - 1 return self[amount:].as_signed() else: return self[amount:] # unsigned diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py index 83bbb77..bcb64f2 100644 --- a/tests/test_hdl_ast.py +++ b/tests/test_hdl_ast.py @@ -343,7 +343,7 @@ class ValueTestCase(FHDLTestCase): self.assertRepr(Const(256, signed(9)).shift_left(-5), "(s (slice (const 9'sd-256) 5:9))") self.assertRepr(Const(256, signed(9)).shift_left(-15), - "(s (slice (const 9'sd-256) 9:9))") + "(s (slice (const 9'sd-256) 8:9))") def test_shift_left_wrong(self): with self.assertRaisesRegex(TypeError, @@ -367,12 +367,20 @@ class ValueTestCase(FHDLTestCase): "(slice (const 9'd256) 1:9)") self.assertRepr(Const(256, unsigned(9)).shift_right(5), "(slice (const 9'd256) 5:9)") + self.assertRepr(Const(256, unsigned(9)).shift_right(15), + "(slice (const 9'd256) 9:9)") self.assertRepr(Const(256, signed(9)).shift_right(1), "(s (slice (const 9'sd-256) 1:9))") self.assertRepr(Const(256, signed(9)).shift_right(5), "(s (slice (const 9'sd-256) 5:9))") + self.assertRepr(Const(256, signed(9)).shift_right(7), + "(s (slice (const 9'sd-256) 7:9))") + self.assertRepr(Const(256, signed(9)).shift_right(8), + "(s (slice (const 9'sd-256) 8:9))") + self.assertRepr(Const(256, signed(9)).shift_right(9), + "(s (slice (const 9'sd-256) 8:9))") self.assertRepr(Const(256, signed(9)).shift_right(15), - "(s (slice (const 9'sd-256) 9:9))") + "(s (slice (const 9'sd-256) 8:9))") def test_shift_right_wrong(self): with self.assertRaisesRegex(TypeError, @@ -516,6 +524,11 @@ class OperatorTestCase(FHDLTestCase): self.assertEqual(repr(v), "(s (const 4'd1))") self.assertEqual(v.shape(), signed(4)) + def test_as_signed_wrong(self): + with self.assertRaisesRegex(ValueError, + r"^Cannot create a 0-width signed value$"): + Const(0, 0).as_signed() + def test_pos(self): self.assertRepr(+Const(10), "(const 4'd10)")