Repository: amaranth-lang/amaranth (python)

Issue:
TypeError when using Const as reset value of a Signal with a lib.data.Layout
Repro:
```python3
from amaranth import *
from amaranth.lib import data

layout = data.StructLayout({"foo": unsigned(1)})
s = Signal(layout, reset={"foo": Const(0)})
```

Output:
```
Traceback (most recent call last):
  File "/home/jf/src/amaranth/amaranth/hdl/ast.py", line 1040, in __init__
    reset = Const.cast(orig_shape.const(reset))
                       ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jf/src/amaranth/amaranth/lib/data.py", line 242, in const
    key_value = Const(key_value, cast_field_shape)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jf/src/amaranth/amaranth/hdl/ast.py", line 659, in __init__
    self.value = int(value)
                 ^^^^^^^^^^
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'Const'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/repro.py", line 5, in <module>
    s = Signal(layout, reset={"foo": Const(0)})
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jf/src/amaranth/amaranth/hdl/ast.py", line 973, in __call__
    signal = super().__call__(shape, **kwargs, src_loc_at=src_loc_at + 1)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jf/src/amaranth/amaranth/hdl/ast.py", line 1042, in __init__
    raise TypeError("Reset value must be a constant initializer of {!r}"
TypeError: Reset value must be a constant initializer of StructLayout({'foo': unsigned(1)})
```

Pull request description:
lib.data: allow Const as value of Layout.const(...) field
Fixes #838.

This is a potential site for a new diagnostic--I'm not sure though.

Patch:
diff --git a/amaranth/lib/data.py b/amaranth/lib/data.py
index 24bbde4..237cc93 100644
--- a/amaranth/lib/data.py
+++ b/amaranth/lib/data.py
@@ -238,7 +238,7 @@ class Layout(ShapeCastable, metaclass=ABCMeta):
                                      "it casts to, {!r}, and not {!r}"
                                      .format(field.shape, cast_field_shape,
                                              key_value.shape()))
-            else:
+            elif not isinstance(key_value, Const):
                 key_value = Const(key_value, cast_field_shape)
             int_value &= ~(((1 << cast_field_shape.width) - 1) << field.offset)
             int_value |= key_value.value << field.offset
@@ -662,6 +662,10 @@ class View(ValueCastable):
             If ``ShapeCastable.__call__`` does not return a value or a value-castable object.
         """
         if isinstance(self.__layout, ArrayLayout):
+            if not isinstance(key, (int, Value, ValueCastable)):
+                raise TypeError("Views with array layout may only be indexed with an integer "
+                                "or a value, not {!r}"
+                                .format(key))
             shape = self.__layout.elem_shape
             value = self.__target.word_select(key, Shape.cast(self.__layout.elem_shape).width)
         else:
@@ -697,6 +701,9 @@ class View(ValueCastable):
             If the layout does not define a field called ``name``, or if ``name`` starts with
             an underscore.
         """
+        if isinstance(self.__layout, ArrayLayout):
+            raise AttributeError("View of {!r} with an array layout does not have fields"
+                                 .format(self.__target))
         try:
             item = self[name]
         except KeyError:


Test patch:
diff --git a/tests/test_lib_data.py b/tests/test_lib_data.py
index 5ce1120..845d240 100644
--- a/tests/test_lib_data.py
+++ b/tests/test_lib_data.py
@@ -424,6 +424,10 @@ class LayoutTestCase(FHDLTestCase):
                 r"that it casts to, unsigned\(8\), and not unsigned\(1\)$"):
             sl.const({"f": "01"})
 
+    def test_const_field_const(self):
+        sl = StructLayout({"f": unsigned(1)})
+        self.assertRepr(sl.const({"f": Const(1)}), "(const 1'd1)")
+
     def test_signal_reset(self):
         sl = StructLayout({
             "a": unsigned(1),
@@ -611,6 +615,17 @@ class ViewTestCase(FHDLTestCase):
                 r"and may only be accessed by indexing$"):
             Signal(StructLayout({"_c": signed(1)}))._c
 
+    def test_bug_837_array_layout_getitem_str(self):
+        with self.assertRaisesRegex(TypeError,
+                r"^Views with array layout may only be indexed with an integer or a value, "
+                r"not 'reset'$"):
+            Signal(ArrayLayout(unsigned(1), 1), reset=[0])["reset"]
+
+    def test_bug_837_array_layout_getattr(self):
+        with self.assertRaisesRegex(AttributeError,
+                r"^View of \(sig \$signal\) with an array layout does not have fields$"):
+            Signal(ArrayLayout(unsigned(1), 1), reset=[0]).reset
+
 
 class StructTestCase(FHDLTestCase):
     def test_construct(self):