From 6058ad35cf34f9f9203313e8c920749fbd6efab8 Mon Sep 17 00:00:00 2001 From: Wanda Date: Fri, 16 Feb 2024 15:27:27 +0100 Subject: [PATCH] hdl._ast: make `Shape` immutable and hashable. Fixes #1127. --- amaranth/hdl/_ast.py | 15 +++++++++++++-- docs/changes.rst | 1 + tests/test_hdl_ast.py | 10 ++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/amaranth/hdl/_ast.py b/amaranth/hdl/_ast.py index dfa802f..cee94b8 100644 --- a/amaranth/hdl/_ast.py +++ b/amaranth/hdl/_ast.py @@ -61,8 +61,16 @@ class Shape: f"not {width}") if signed and width <= 0: raise TypeError(f"Width of a signed value must be a positive integer, not {width}") - self.width = width - self.signed = bool(signed) + self._width = width + self._signed = bool(signed) + + @property + def width(self): + return self._width + + @property + def signed(self): + return self._signed # The algorithm for inferring shape for standard Python enumerations is factored out so that # `Shape.cast()` and Amaranth's `EnumMeta.as_shape()` can both use it. @@ -144,6 +152,9 @@ class Shape: else: return f"unsigned({self.width})" + def __hash__(self): + return hash((self._width, self._signed)) + def __eq__(self, other): return (isinstance(other, Shape) and self.width == other.width and self.signed == other.signed) diff --git a/docs/changes.rst b/docs/changes.rst index b60e15a..d7e122b 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -63,6 +63,7 @@ Language changes * Changed: ``Signal(range(0))`` is now valid without a warning. * Changed: ``Shape.cast(range(1))`` is now ``unsigned(0)``. (`RFC 46`_) * Changed: the ``reset=`` argument of :class:`Signal`, :meth:`Signal.like`, :class:`amaranth.lib.wiring.Member`, :class:`amaranth.lib.cdc.FFSynchronizer`, and ``m.FSM()`` has been renamed to ``init=``. (`RFC 43`_) +* Changed: :class:`Shape` has been made immutable and hashable. * Deprecated: :func:`amaranth.utils.log2_int`. (`RFC 17`_) * Removed: (deprecated in 0.4) :meth:`Const.normalize`. (`RFC 5`_) * Removed: (deprecated in 0.4) :class:`Repl`. (`RFC 10`_) diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py index 6740c9e..54a7d02 100644 --- a/tests/test_hdl_ast.py +++ b/tests/test_hdl_ast.py @@ -152,6 +152,16 @@ class ShapeTestCase(FHDLTestCase): r"^Object 'foo' cannot be converted to an Amaranth shape$"): Shape.cast("foo") + def test_hashable(self): + d = { + signed(2): "a", + unsigned(3): "b", + unsigned(2): "c", + } + self.assertEqual(d[signed(2)], "a") + self.assertEqual(d[unsigned(3)], "b") + self.assertEqual(d[unsigned(2)], "c") + class MockShapeCastable(ShapeCastable): def __init__(self, dest):