@@ -187,6 +187,20 @@ def __eq__(self, other):
187187 return (isinstance (other , Layout ) and self .size == other .size and
188188 dict (iter (self )) == dict (iter (other )))
189189
190+ def __call__ (self , target ):
191+ """Create a view into a target.
192+
193+ When a :class:`Layout` is used as the shape of a :class:`Field` and accessed through
194+ a :class:`View`, this method is used to wrap the slice of the underlying value into
195+ another view with this layout.
196+
197+ Returns
198+ -------
199+ View
200+ ``View(self, target)``
201+ """
202+ return View (self , target )
203+
190204 def _convert_to_int (self , value ):
191205 """Convert ``value``, which may be a dict or an array of field values, to an integer using
192206 the representation defined by this layout.
@@ -560,10 +574,12 @@ class View(ValueCastable):
560574 ################
561575
562576 Slicing a view or accessing its attributes returns a part of the underlying value
563- corresponding to the field with that index or name, which is always an Amaranth value, but
564- it could also be a :class:`View` if the shape of the field is a :class:`Layout`, or
565- an instance of the data class if the shape of the field is a class deriving from
566- :class:`Struct` or :class:`Union`.
577+ corresponding to the field with that index or name, which is itself either a value or
578+ a value-castable object. If the shape of the field is a :class:`Layout`, it will be
579+ a :class:`View`; if it is a class deriving from :class:`Struct` or :class:`Union`, it
580+ will be an instance of that data class; if it is another
581+ :ref:`shape-castable <lang-shapecasting>` object implementing ``__call__``, it will be
582+ the result of calling that method.
567583
568584 Slicing a view whose layout is an :class:`ArrayLayout` can be done with an index that is
569585 an Amaranth value instead of a constant integer. The returned element is chosen dynamically
@@ -639,9 +655,10 @@ def __getitem__(self, key):
639655 """Slice the underlying value.
640656
641657 A field corresponding to ``key`` is looked up in the layout. If the field's shape is
642- a :class:`Layout`, returns a :class:`View`. If it is a subclass of :class:`Struct` or
643- :class:`Union`, returns an instance of that class. Otherwise, returns an unspecified
644- Amaranth expression with the right shape.
658+ a shape-castable object that has a ``__call__`` method, it is called and the result is
659+ returned. Otherwise, ``as_shape`` is called repeatedly on the shape until either an object
660+ with a ``__call__`` method is reached, or a ``Shape`` is returned. In the latter case,
661+ returns an unspecified Amaranth expression with the right shape.
645662
646663 Arguments
647664 ---------
@@ -650,7 +667,7 @@ def __getitem__(self, key):
650667
651668 Returns
652669 -------
653- :class:`Value`, inout
670+ :class:`Value` or :class:`ValueCastable` , inout
654671 A slice of the underlying value defined by the field.
655672
656673 Raises
@@ -660,6 +677,8 @@ def __getitem__(self, key):
660677 TypeError
661678 If ``key`` is a value-castable object, but the layout of the view is not
662679 a :class:`ArrayLayout`.
680+ TypeError
681+ If ``ShapeCastable.__call__`` does not return a value or a value-castable object.
663682 """
664683 if isinstance (self .__layout , ArrayLayout ):
665684 shape = self .__layout .elem_shape
@@ -672,10 +691,16 @@ def __getitem__(self, key):
672691 field = self .__layout [key ]
673692 shape = field .shape
674693 value = self .__target [field .offset :field .offset + field .width ]
675- if isinstance (shape , _AggregateMeta ):
676- return shape (value )
677- if isinstance (shape , Layout ):
678- return View (shape , value )
694+ # Field guarantees that the shape-castable object is well-formed, so there is no need
695+ # to handle erroneous cases here.
696+ while isinstance (shape , ShapeCastable ):
697+ if hasattr (shape , "__call__" ):
698+ value = shape (value )
699+ if not isinstance (value , (Value , ValueCastable )):
700+ raise TypeError ("{!r}.__call__() must return a value or "
701+ "a value-castable object, not {!r}"
702+ .format (shape , value ))
703+ return value
679704 if Shape .cast (shape ).signed :
680705 return value .as_signed ()
681706 else :
0 commit comments