From: whitequark Date: Sat, 11 Dec 2021 12:39:34 +0000 (+0000) Subject: hdl.ir: reject elaboratables that elaborate to themselves. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d839aff60ae822cb6f6bf3b5bd8410dc26e45673;p=nmigen.git hdl.ir: reject elaboratables that elaborate to themselves. Fixes #592. --- diff --git a/nmigen/hdl/ir.py b/nmigen/hdl/ir.py index 3c1ec60..45857b8 100644 --- a/nmigen/hdl/ir.py +++ b/nmigen/hdl/ir.py @@ -34,7 +34,7 @@ class Fragment: elif isinstance(obj, Elaboratable): code = obj.elaborate.__code__ obj._MustUse__used = True - obj = obj.elaborate(platform) + new_obj = obj.elaborate(platform) elif hasattr(obj, "elaborate"): warnings.warn( message="Class {!r} is an elaboratable that does not explicitly inherit from " @@ -43,15 +43,18 @@ class Fragment: category=RuntimeWarning, stacklevel=2) code = obj.elaborate.__code__ - obj = obj.elaborate(platform) + new_obj = obj.elaborate(platform) else: raise AttributeError("Object {!r} cannot be elaborated".format(obj)) - if obj is None and code is not None: + if new_obj is obj: + raise RecursionError("Object {!r} elaborates to itself".format(obj)) + if new_obj is None and code is not None: warnings.warn_explicit( message=".elaborate() returned None; missing return statement?", category=UserWarning, filename=code.co_filename, lineno=code.co_firstlineno) + obj = new_obj def __init__(self): self.ports = SignalDict() diff --git a/tests/test_hdl_ir.py b/tests/test_hdl_ir.py index c578bbe..ce2c255 100644 --- a/tests/test_hdl_ir.py +++ b/tests/test_hdl_ir.py @@ -10,13 +10,18 @@ from nmigen.hdl.mem import * from .utils import * -class BadElaboratable(Elaboratable): +class ElaboratesToNone(Elaboratable): def elaborate(self, platform): return +class ElaboratesToSelf(Elaboratable): + def elaborate(self, platform): + return self + + class FragmentGetTestCase(FHDLTestCase): - def test_get_wrong(self): + def test_get_wrong_none(self): with self.assertRaisesRegex(AttributeError, r"^Object None cannot be elaborated$"): Fragment.get(None, platform=None) @@ -25,7 +30,12 @@ class FragmentGetTestCase(FHDLTestCase): r"^\.elaborate\(\) returned None; missing return statement\?$"): with self.assertRaisesRegex(AttributeError, r"^Object None cannot be elaborated$"): - Fragment.get(BadElaboratable(), platform=None) + Fragment.get(ElaboratesToNone(), platform=None) + + def test_get_wrong_self(self): + with self.assertRaisesRegex(RecursionError, + r"^Object <.+?ElaboratesToSelf.+?> elaborates to itself$"): + Fragment.get(ElaboratesToSelf(), platform=None) class FragmentGeneratedTestCase(FHDLTestCase):