Repository: amaranth-lang/amaranth (python)

Issue:
`ShapeCastable()` is a valid expression
This shouldn't be possible to do since it's an abstract class.

Pull request description:
hdl.ast: make it impossible to construct `*Castable` instances.
Fixes #1072.

Patch:
diff --git a/amaranth/hdl/_ast.py b/amaranth/hdl/_ast.py
index c2fe98f..5084574 100644
--- a/amaranth/hdl/_ast.py
+++ b/amaranth/hdl/_ast.py
@@ -191,6 +191,12 @@ class ShapeCastable:
         The source code of the :mod:`amaranth.lib.data` module can be used as a reference for
         implementing a fully featured shape-castable object.
     """
+
+    def __init__(self, *args, **kwargs):
+        if type(self) is ShapeCastable:
+            raise TypeError("Can't instantiate abstract class ShapeCastable")
+        super().__init__(*args, **kwargs)
+
     def __init_subclass__(cls, **kwargs):
         if cls.as_shape is ShapeCastable.as_shape:
             raise TypeError(f"Class '{cls.__name__}' deriving from 'ShapeCastable' must override "
@@ -1616,6 +1622,12 @@ class ValueCastable:
     from :class:`ValueCastable` is mutable, it is up to the user to ensure that it is not mutated
     in a way that changes its representation after the first call to :meth:`as_value`.
     """
+
+    def __init__(self, *args, **kwargs):
+        if type(self) is ValueCastable:
+            raise TypeError("Can't instantiate abstract class ValueCastable")
+        super().__init__(*args, **kwargs)
+
     def __init_subclass__(cls, **kwargs):
         if not hasattr(cls, "as_value"):
             raise TypeError(f"Class '{cls.__name__}' deriving from `ValueCastable` must override "


Test patch:
diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py
index 8829aa7..14e1f8a 100644
--- a/tests/test_hdl_ast.py
+++ b/tests/test_hdl_ast.py
@@ -191,6 +191,11 @@ class ShapeCastableTestCase(FHDLTestCase):
         sc = MockShapeCastable(MockShapeCastable(unsigned(1)))
         self.assertEqual(Shape.cast(sc), unsigned(1))
 
+    def test_abstract(self):
+        with self.assertRaisesRegex(TypeError,
+                r"^Can't instantiate abstract class ShapeCastable$"):
+            ShapeCastable()
+
 
 class ShapeLikeTestCase(FHDLTestCase):
     def test_construct(self):
@@ -1400,6 +1405,11 @@ class ValueCastableTestCase(FHDLTestCase):
         vc = MockValueCastable(MockValueCastable(Signal()))
         self.assertIsInstance(Value.cast(vc), Signal)
 
+    def test_abstract(self):
+        with self.assertRaisesRegex(TypeError,
+                r"^Can't instantiate abstract class ValueCastable$"):
+            ValueCastable()
+
 
 class ValueLikeTestCase(FHDLTestCase):
     def test_construct(self):