From: Luke Kenneth Casson Leighton Date: Thu, 30 Sep 2021 12:50:18 +0000 (+0100) Subject: add Value.considered_bool() function X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d15841126c390f5981651872e0eb5d5bf0e7b28f;p=nmigen.git add Value.considered_bool() function testing len(Value) == 1 is one of the rare places that makes an assumption between Type 1 (Ast) low-level nmigen language constructs and between Type 2 (dsl.Module) high-level nmigen language constructs. a "Value.considered_bool()" function de-couples that assumption and allows for a proper separation between Type 1 and Type 2 nmigen language constructs, providing a clean design where Liskov Substitution Principle may successfully be applied at the Type I (ast) level --- diff --git a/nmigen/compat/fhdl/structure.py b/nmigen/compat/fhdl/structure.py index 68ddc49..5a29b00 100644 --- a/nmigen/compat/fhdl/structure.py +++ b/nmigen/compat/fhdl/structure.py @@ -123,14 +123,14 @@ class If(ast._InternalSwitch): @deprecated("instead of `If(cond, ...)`, use `with m.If(cond): ...`") def __init__(self, cond, *stmts): cond = Value.cast(cond) - if len(cond) != 1: + if not cond.considered_bool(): cond = cond.bool() super().__init__(cond, {("1",): ast.Statement.cast(stmts)}) @deprecated("instead of `.Elif(cond, ...)`, use `with m.Elif(cond): ...`") def Elif(self, cond, *stmts): cond = Value.cast(cond) - if len(cond) != 1: + if not cond.considered_bool(): cond = cond.bool() self.cases = OrderedDict((("-" + k,), v) for (k,), v in self.cases.items()) self.cases[("1" + "-" * len(self.test),)] = ast.Statement.cast(stmts) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index 5045844..5f3f5bc 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -175,6 +175,9 @@ class Value(metaclass=ABCMeta): def __Assign__(self, rhs, *, src_loc_at=0): return _InternalAssign(self, rhs, src_loc_at=src_loc_at) + def considered_bool(self): + return len(self) == 1 + def __bool__(self): raise TypeError("Attempted to convert nMigen value to Python boolean") @@ -767,7 +770,8 @@ def _InternalMux(sel, val1, val0): Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``. """ sel = Value.cast(sel) - if len(sel) != 1: + # test whether the value is considered boolean, and if not, convert it + if not sel.considered_bool(): sel = sel.bool() return Operator("m", [sel, val1, val0]) diff --git a/nmigen/hdl/dsl.py b/nmigen/hdl/dsl.py index c447369..337e176 100644 --- a/nmigen/hdl/dsl.py +++ b/nmigen/hdl/dsl.py @@ -433,9 +433,13 @@ class Module(_ModuleBuilderRoot, Elaboratable): tests, cases = [], OrderedDict() for if_test, if_case in zip(if_tests + [None], if_bodies): if if_test is not None: - if_test = Value.cast(if_test) - if len(if_test) != 1: - if_test = if_test.bool() + # check if the if_test is not considered boolean, and only + # append a converted version if it is not. this defers the + # Value.cast (which would lose type if done mandatorially). + # ast._InternalSwitch does a (second) Value.cast anyway + _if_test = Value.cast(if_test) + if not _if_test.considered_bool(): + if_test = _if_test.bool() tests.append(if_test) if if_test is not None: