Implement RFC 31: Enumeration type safety.

This commit is contained in:
Wanda 2023-11-22 07:35:55 +01:00 committed by Catherine
parent b0b193f1ad
commit ef5cfa72bc
4 changed files with 456 additions and 22 deletions

View file

@ -60,6 +60,7 @@ Implemented RFCs
.. _RFC 20: https://amaranth-lang.org/rfcs/0020-deprecate-non-fwft-fifos.html
.. _RFC 22: https://amaranth-lang.org/rfcs/0022-valuecastable-shape.html
.. _RFC 28: https://amaranth-lang.org/rfcs/0028-override-value-operators.html
.. _RFC 31: https://amaranth-lang.org/rfcs/0031-enumeration-type-safety.html
* `RFC 1`_: Aggregate data structure library
@ -77,6 +78,7 @@ Implemented RFCs
* `RFC 20`_: Deprecate non-FWFT FIFOs
* `RFC 22`_: Define ``ValueCastable.shape()``
* `RFC 28`_: Allow overriding ``Value`` operators
* `RFC 31`_: Enumeration type safety
Language changes

View file

@ -24,6 +24,8 @@ A shape can be specified for an enumeration with the ``shape=`` keyword argument
>>> Shape.cast(Funct)
unsigned(4)
>>> Value.cast(Funct.ADD)
(const 4'd0)
Any :ref:`constant-castable <lang-constcasting>` expression can be used as the value of a member:
@ -57,6 +59,57 @@ The ``shape=`` argument is optional. If not specified, classes from this module
In this way, this module is a drop-in replacement for the standard :mod:`enum` module, and in an Amaranth project, all ``import enum`` statements may be replaced with ``from amaranth.lib import enum``.
Signals with :class:`Enum` or :class:`Flag` based shape are automatically wrapped in the :class:`EnumView` or :class:`FlagView` value-castable wrappers, which ensure type safety. Any :ref:`value-castable <lang-valuecasting>` can also be explicitly wrapped in a view class by casting it to the enum type:
.. doctest::
>>> a = Signal(Funct)
>>> b = Signal(Op)
>>> type(a)
<class 'amaranth.lib.enum.EnumView'>
>>> a == b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: an EnumView can only be compared to value or other EnumView of the same enum type
>>> c = Signal(4)
>>> type(Funct(c))
<class 'amaranth.lib.enum.EnumView'>
Like the standard Python :class:`enum.IntEnum` and :class:`enum.IntFlag` classes, the Amaranth :class:`IntEnum` and :class:`IntFlag` classes are loosely typed and will not be subject to wrapping in view classes:
.. testcode::
class TransparentEnum(enum.IntEnum, shape=unsigned(4)):
FOO = 0
BAR = 1
.. doctest::
>>> a = Signal(TransparentEnum)
>>> type(a)
<class 'amaranth.hdl.ast.Signal'>
It is also possible to define a custom view class for a given enum:
.. testcode::
class InstrView(enum.EnumView):
def has_immediate(self):
return (self == Instr.ADDI) | (self == Instr.SUBI)
class Instr(enum.Enum, shape=5, view_class=InstrView):
ADD = Cat(Funct.ADD, Op.REG)
ADDI = Cat(Funct.ADD, Op.IMM)
SUB = Cat(Funct.SUB, Op.REG)
SUBI = Cat(Funct.SUB, Op.IMM)
.. doctest::
>>> a = Signal(Instr)
>>> type(a)
<class 'InstrView'>
>>> a.has_immediate()
(| (== (sig a) (const 5'd16)) (== (sig a) (const 5'd17)))
Metaclass
=========
@ -71,3 +124,9 @@ Base classes
.. autoclass:: IntEnum()
.. autoclass:: Flag()
.. autoclass:: IntFlag()
View classes
============
.. autoclass:: EnumView()
.. autoclass:: FlagView()