hdl.ir: reject elaboratables that elaborate to themselves.

Fixes #592.
This commit is contained in:
whitequark 2021-12-11 12:39:34 +00:00
parent 90777a65c8
commit 599615ee3a
2 changed files with 19 additions and 6 deletions

View file

@ -34,7 +34,7 @@ class Fragment:
elif isinstance(obj, Elaboratable): elif isinstance(obj, Elaboratable):
code = obj.elaborate.__code__ code = obj.elaborate.__code__
obj._MustUse__used = True obj._MustUse__used = True
obj = obj.elaborate(platform) new_obj = obj.elaborate(platform)
elif hasattr(obj, "elaborate"): elif hasattr(obj, "elaborate"):
warnings.warn( warnings.warn(
message="Class {!r} is an elaboratable that does not explicitly inherit from " message="Class {!r} is an elaboratable that does not explicitly inherit from "
@ -43,15 +43,18 @@ class Fragment:
category=RuntimeWarning, category=RuntimeWarning,
stacklevel=2) stacklevel=2)
code = obj.elaborate.__code__ code = obj.elaborate.__code__
obj = obj.elaborate(platform) new_obj = obj.elaborate(platform)
else: else:
raise AttributeError("Object {!r} cannot be elaborated".format(obj)) 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( warnings.warn_explicit(
message=".elaborate() returned None; missing return statement?", message=".elaborate() returned None; missing return statement?",
category=UserWarning, category=UserWarning,
filename=code.co_filename, filename=code.co_filename,
lineno=code.co_firstlineno) lineno=code.co_firstlineno)
obj = new_obj
def __init__(self): def __init__(self):
self.ports = SignalDict() self.ports = SignalDict()

View file

@ -10,13 +10,18 @@ from amaranth.hdl.mem import *
from .utils import * from .utils import *
class BadElaboratable(Elaboratable): class ElaboratesToNone(Elaboratable):
def elaborate(self, platform): def elaborate(self, platform):
return return
class ElaboratesToSelf(Elaboratable):
def elaborate(self, platform):
return self
class FragmentGetTestCase(FHDLTestCase): class FragmentGetTestCase(FHDLTestCase):
def test_get_wrong(self): def test_get_wrong_none(self):
with self.assertRaisesRegex(AttributeError, with self.assertRaisesRegex(AttributeError,
r"^Object None cannot be elaborated$"): r"^Object None cannot be elaborated$"):
Fragment.get(None, platform=None) Fragment.get(None, platform=None)
@ -25,7 +30,12 @@ class FragmentGetTestCase(FHDLTestCase):
r"^\.elaborate\(\) returned None; missing return statement\?$"): r"^\.elaborate\(\) returned None; missing return statement\?$"):
with self.assertRaisesRegex(AttributeError, with self.assertRaisesRegex(AttributeError,
r"^Object None cannot be elaborated$"): 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): class FragmentGeneratedTestCase(FHDLTestCase):