+class ValueCastable:
+ """Base class for classes which can be cast to Values.
+
+ A ``ValueCastable`` can be cast to ``Value``, meaning its precise representation does not have
+ to be immediately known. This is useful in certain metaprogramming scenarios. Instead of
+ providing fixed semantics upfront, it is kept abstract for as long as possible, only being
+ cast to a concrete nMigen value when required.
+
+ Note that it is necessary to ensure that nMigen's view of representation of all values stays
+ internally consistent. The class deriving from ``ValueCastable`` must decorate the ``as_value``
+ method with the ``lowermethod`` decorator, which ensures that all calls to ``as_value``return the
+ same ``Value`` representation. If the class deriving from ``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 ``as_value``.
+ """
+ def __new__(cls, *args, **kwargs):
+ self = super().__new__(cls)
+ if not hasattr(self, "as_value"):
+ raise TypeError(f"Class '{cls.__name__}' deriving from `ValueCastable` must override the `as_value` method")
+
+ if not hasattr(self.as_value, "_ValueCastable__memoized"):
+ raise TypeError(f"Class '{cls.__name__}' deriving from `ValueCastable` must decorate the `as_value` "
+ "method with the `ValueCastable.lowermethod` decorator")
+ return self
+
+ @staticmethod
+ def lowermethod(func):
+ """Decorator to memoize lowering methods.
+
+ Ensures the decorated method is called only once, with subsequent method calls returning the
+ object returned by the first first method call.
+
+ This decorator is required to decorate the ``as_value`` method of ``ValueCastable`` subclasses.
+ This is to ensure that nMigen's view of representation of all values stays internally
+ consistent.
+ """
+ @functools.wraps(func)
+ def wrapper_memoized(self, *args, **kwargs):
+ if not hasattr(self, "_ValueCastable__lowered_to"):
+ self.__lowered_to = func(self, *args, **kwargs)
+ return self.__lowered_to
+ wrapper_memoized.__memoized = True
+ return wrapper_memoized
+
+