From f82a09608220a7024b5b2d837f0efbe5ecf9707e Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 12 May 2019 05:36:35 +0000 Subject: [PATCH] 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. --- nmigen/hdl/ast.py | 20 ++++++++++++++++++++ nmigen/hdl/rec.py | 1 + nmigen/tools.py | 10 +++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index 37816ce..c246af1 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -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() diff --git a/nmigen/hdl/rec.py b/nmigen/hdl/rec.py index 2ab00c8..63a977c 100644 --- a/nmigen/hdl/rec.py +++ b/nmigen/hdl/rec.py @@ -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: diff --git a/nmigen/tools.py b/nmigen/tools.py index edf2195..09c9d31 100644 --- a/nmigen/tools.py +++ b/nmigen/tools.py @@ -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) -- 2.30.2