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:
parent
958cb18b88
commit
744e33f42d
|
@ -210,6 +210,7 @@ class Value(metaclass=ABCMeta):
|
||||||
__hash__ = None
|
__hash__ = None
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Const(Value):
|
class Const(Value):
|
||||||
"""A constant, literal integer value.
|
"""A constant, literal integer value.
|
||||||
|
|
||||||
|
@ -283,16 +284,19 @@ class AnyValue(Value, DUID):
|
||||||
return ValueSet()
|
return ValueSet()
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class AnyConst(AnyValue):
|
class AnyConst(AnyValue):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "(anyconst {}'{})".format(self.nbits, "s" if self.signed else "")
|
return "(anyconst {}'{})".format(self.nbits, "s" if self.signed else "")
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class AnySeq(AnyValue):
|
class AnySeq(AnyValue):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "(anyseq {}'{})".format(self.nbits, "s" if self.signed else "")
|
return "(anyseq {}'{})".format(self.nbits, "s" if self.signed else "")
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Operator(Value):
|
class Operator(Value):
|
||||||
def __init__(self, op, operands, src_loc_at=0):
|
def __init__(self, op, operands, src_loc_at=0):
|
||||||
super().__init__(src_loc_at=1 + src_loc_at)
|
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)
|
return Operator("m", [sel, val1, val0], src_loc_at=1)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Slice(Value):
|
class Slice(Value):
|
||||||
def __init__(self, value, start, end):
|
def __init__(self, value, start, end):
|
||||||
if not isinstance(start, int):
|
if not isinstance(start, int):
|
||||||
|
@ -424,6 +429,7 @@ class Slice(Value):
|
||||||
return "(slice {} {}:{})".format(repr(self.value), self.start, self.end)
|
return "(slice {} {}:{})".format(repr(self.value), self.start, self.end)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Part(Value):
|
class Part(Value):
|
||||||
def __init__(self, value, offset, width):
|
def __init__(self, value, offset, width):
|
||||||
if not isinstance(width, int) or width < 0:
|
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)
|
return "(part {} {} {})".format(repr(self.value), repr(self.offset), self.width)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Cat(Value):
|
class Cat(Value):
|
||||||
"""Concatenate values.
|
"""Concatenate values.
|
||||||
|
|
||||||
|
@ -495,6 +502,7 @@ class Cat(Value):
|
||||||
return "(cat {})".format(" ".join(map(repr, self.parts)))
|
return "(cat {})".format(" ".join(map(repr, self.parts)))
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Repl(Value):
|
class Repl(Value):
|
||||||
"""Replicate a value
|
"""Replicate a value
|
||||||
|
|
||||||
|
@ -534,6 +542,7 @@ class Repl(Value):
|
||||||
return "(repl {!r} {})".format(self.value, self.count)
|
return "(repl {!r} {})".format(self.value, self.count)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Signal(Value, DUID):
|
class Signal(Value, DUID):
|
||||||
"""A varying integer value.
|
"""A varying integer value.
|
||||||
|
|
||||||
|
@ -649,6 +658,7 @@ class Signal(Value, DUID):
|
||||||
return "(sig {})".format(self.name)
|
return "(sig {})".format(self.name)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class ClockSignal(Value):
|
class ClockSignal(Value):
|
||||||
"""Clock signal for a clock domain.
|
"""Clock signal for a clock domain.
|
||||||
|
|
||||||
|
@ -680,6 +690,7 @@ class ClockSignal(Value):
|
||||||
return "(clk {})".format(self.domain)
|
return "(clk {})".format(self.domain)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class ResetSignal(Value):
|
class ResetSignal(Value):
|
||||||
"""Reset signal for a clock domain.
|
"""Reset signal for a clock domain.
|
||||||
|
|
||||||
|
@ -802,6 +813,7 @@ class Array(MutableSequence):
|
||||||
", ".join(map(repr, self._inner)))
|
", ".join(map(repr, self._inner)))
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class ArrayProxy(Value):
|
class ArrayProxy(Value):
|
||||||
def __init__(self, elems, index):
|
def __init__(self, elems, index):
|
||||||
super().__init__(src_loc_at=1)
|
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)
|
return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self.elems)), self.index)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Sample(Value):
|
class Sample(Value):
|
||||||
"""Value from the past.
|
"""Value from the past.
|
||||||
|
|
||||||
|
@ -899,6 +912,7 @@ class Statement:
|
||||||
raise TypeError("Object '{!r}' is not an nMigen statement".format(obj))
|
raise TypeError("Object '{!r}' is not an nMigen statement".format(obj))
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Assign(Statement):
|
class Assign(Statement):
|
||||||
def __init__(self, lhs, rhs):
|
def __init__(self, lhs, rhs):
|
||||||
self.lhs = Value.wrap(lhs)
|
self.lhs = Value.wrap(lhs)
|
||||||
|
@ -940,14 +954,17 @@ class Property(Statement):
|
||||||
return "({} {!r})".format(self._kind, self.test)
|
return "({} {!r})".format(self._kind, self.test)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Assert(Property):
|
class Assert(Property):
|
||||||
_kind = "assert"
|
_kind = "assert"
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Assume(Property):
|
class Assume(Property):
|
||||||
_kind = "assume"
|
_kind = "assume"
|
||||||
|
|
||||||
|
|
||||||
|
# @final
|
||||||
class Switch(Statement):
|
class Switch(Statement):
|
||||||
def __init__(self, test, cases):
|
def __init__(self, test, cases):
|
||||||
self.test = Value.wrap(test)
|
self.test = Value.wrap(test)
|
||||||
|
@ -981,6 +998,7 @@ class Switch(Statement):
|
||||||
return "(switch {!r} {})".format(self.test, " ".join(cases))
|
return "(switch {!r} {})".format(self.test, " ".join(cases))
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Delay(Statement):
|
class Delay(Statement):
|
||||||
def __init__(self, interval=None):
|
def __init__(self, interval=None):
|
||||||
self.interval = None if interval is None else float(interval)
|
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)
|
return "(delay {:.3}us)".format(self.interval * 1e6)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Tick(Statement):
|
class Tick(Statement):
|
||||||
def __init__(self, domain="sync"):
|
def __init__(self, domain="sync"):
|
||||||
self.domain = str(domain)
|
self.domain = str(domain)
|
||||||
|
@ -1006,6 +1025,7 @@ class Tick(Statement):
|
||||||
return "(tick {})".format(self.domain)
|
return "(tick {})".format(self.domain)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
class Passive(Statement):
|
class Passive(Statement):
|
||||||
def _rhs_signals(self):
|
def _rhs_signals(self):
|
||||||
return ValueSet()
|
return ValueSet()
|
||||||
|
|
|
@ -62,6 +62,7 @@ class Layout:
|
||||||
yield (name, shape, dir)
|
yield (name, shape, dir)
|
||||||
|
|
||||||
|
|
||||||
|
# Unlike most Values, Record *can* be subclassed.
|
||||||
class Record(Value):
|
class Record(Value):
|
||||||
def __init__(self, layout, name=None):
|
def __init__(self, layout, name=None):
|
||||||
if name is None:
|
if name is None:
|
||||||
|
|
|
@ -6,7 +6,7 @@ from collections.abc import Iterable
|
||||||
from contextlib import contextmanager
|
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):
|
def flatten(i):
|
||||||
|
@ -57,6 +57,14 @@ def memoize(f):
|
||||||
return g
|
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 deprecated(message, stacklevel=2):
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
@functools.wraps(f)
|
@functools.wraps(f)
|
||||||
|
|
Loading…
Reference in a new issue