Repository: amaranth-lang/amaranth (python)

Issue:
TypeError when accessing reset attribute from a Signal created with an ArrayLayout
Repro:
```python3
from amaranth import *
from amaranth.lib import data

layout = data.ArrayLayout(unsigned(1), 4)
a = Signal(layout, reset=[0,0,0,0])
a.reset
```

Output:
```
Traceback (most recent call last):
  File "/tmp/repro.py", line 7, in <module>
    a.reset
  File "/home/jf/src/amaranth/amaranth/lib/data.py", line 701, in __getattr__
    item = self[name]
           ~~~~^^^^^^
  File "/home/jf/src/amaranth/amaranth/lib/data.py", line 666, in __getitem__
    value = self.__target.word_select(key, Shape.cast(self.__layout.elem_shape).width)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jf/src/amaranth/amaranth/hdl/ast.py", line 400, in word_select
    offset = Value.cast(offset)
             ^^^^^^^^^^^^^^^^^^
  File "/home/jf/src/amaranth/amaranth/hdl/ast.py", line 174, in cast
    raise TypeError("Object {!r} cannot be converted to an Amaranth value".format(obj))
TypeError: Object 'reset' cannot be converted to an Amaranth value
```

Pull request description:
lib.data: improve diagnostics for field access on array layout view
Fixes #837.

Patch:
diff --git a/amaranth/lib/data.py b/amaranth/lib/data.py
index 24bbde4..1013863 100644
--- a/amaranth/lib/data.py
+++ b/amaranth/lib/data.py
@@ -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..e368095 100644
--- a/tests/test_lib_data.py
+++ b/tests/test_lib_data.py
@@ -611,6 +611,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):