hdl: make all public Value classes other than Record final.

In some cases, nMigen uses type() instead of isinstance() to dispatch
on types. Make sure all such uses of type() are robust; in addition,
make it clear that nMigen AST classes are not meant to be subclassed.
(Record is an exception.)

Fixes #65.
This commit is contained in:
whitequark 2019-05-12 05:36:35 +00:00
parent 958cb18b88
commit 744e33f42d
3 changed files with 30 additions and 1 deletions

View file

@ -210,6 +210,7 @@ class Value(metaclass=ABCMeta):
__hash__ = None
@final
class Const(Value):
"""A constant, literal integer value.
@ -283,16 +284,19 @@ class AnyValue(Value, DUID):
return ValueSet()
@final
class AnyConst(AnyValue):
def __repr__(self):
return "(anyconst {}'{})".format(self.nbits, "s" if self.signed else "")
@final
class AnySeq(AnyValue):
def __repr__(self):
return "(anyseq {}'{})".format(self.nbits, "s" if self.signed else "")
@final
class Operator(Value):
def __init__(self, op, operands, src_loc_at=0):
super().__init__(src_loc_at=1 + src_loc_at)
@ -387,6 +391,7 @@ def Mux(sel, val1, val0):
return Operator("m", [sel, val1, val0], src_loc_at=1)
@final
class Slice(Value):
def __init__(self, value, start, end):
if not isinstance(start, int):
@ -424,6 +429,7 @@ class Slice(Value):
return "(slice {} {}:{})".format(repr(self.value), self.start, self.end)
@final
class Part(Value):
def __init__(self, value, offset, width):
if not isinstance(width, int) or width < 0:
@ -447,6 +453,7 @@ class Part(Value):
return "(part {} {} {})".format(repr(self.value), repr(self.offset), self.width)
@final
class Cat(Value):
"""Concatenate values.
@ -495,6 +502,7 @@ class Cat(Value):
return "(cat {})".format(" ".join(map(repr, self.parts)))
@final
class Repl(Value):
"""Replicate a value
@ -534,6 +542,7 @@ class Repl(Value):
return "(repl {!r} {})".format(self.value, self.count)
@final
class Signal(Value, DUID):
"""A varying integer value.
@ -649,6 +658,7 @@ class Signal(Value, DUID):
return "(sig {})".format(self.name)
@final
class ClockSignal(Value):
"""Clock signal for a clock domain.
@ -680,6 +690,7 @@ class ClockSignal(Value):
return "(clk {})".format(self.domain)
@final
class ResetSignal(Value):
"""Reset signal for a clock domain.
@ -802,6 +813,7 @@ class Array(MutableSequence):
", ".join(map(repr, self._inner)))
@final
class ArrayProxy(Value):
def __init__(self, elems, index):
super().__init__(src_loc_at=1)
@ -836,6 +848,7 @@ class ArrayProxy(Value):
return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self.elems)), self.index)
@final
class Sample(Value):
"""Value from the past.
@ -899,6 +912,7 @@ class Statement:
raise TypeError("Object '{!r}' is not an nMigen statement".format(obj))
@final
class Assign(Statement):
def __init__(self, lhs, rhs):
self.lhs = Value.wrap(lhs)
@ -940,14 +954,17 @@ class Property(Statement):
return "({} {!r})".format(self._kind, self.test)
@final
class Assert(Property):
_kind = "assert"
@final
class Assume(Property):
_kind = "assume"
# @final
class Switch(Statement):
def __init__(self, test, cases):
self.test = Value.wrap(test)
@ -981,6 +998,7 @@ class Switch(Statement):
return "(switch {!r} {})".format(self.test, " ".join(cases))
@final
class Delay(Statement):
def __init__(self, interval=None):
self.interval = None if interval is None else float(interval)
@ -995,6 +1013,7 @@ class Delay(Statement):
return "(delay {:.3}us)".format(self.interval * 1e6)
@final
class Tick(Statement):
def __init__(self, domain="sync"):
self.domain = str(domain)
@ -1006,6 +1025,7 @@ class Tick(Statement):
return "(tick {})".format(self.domain)
@final
class Passive(Statement):
def _rhs_signals(self):
return ValueSet()

View file

@ -62,6 +62,7 @@ class Layout:
yield (name, shape, dir)
# Unlike most Values, Record *can* be subclassed.
class Record(Value):
def __init__(self, layout, name=None):
if name is None:

View file

@ -6,7 +6,7 @@ from collections.abc import Iterable
from contextlib import contextmanager
__all__ = ["flatten", "union", "log2_int", "bits_for", "memoize", "deprecated"]
__all__ = ["flatten", "union", "log2_int", "bits_for", "memoize", "final", "deprecated"]
def flatten(i):
@ -57,6 +57,14 @@ def memoize(f):
return g
def final(cls):
def init_subclass():
raise TypeError("Subclassing {}.{} is not supported"
.format(cls.__module__, cls.__name__))
cls.__init_subclass__ = init_subclass
return cls
def deprecated(message, stacklevel=2):
def decorator(f):
@functools.wraps(f)