hdl.dsl: make if m.{If,Elif,Else}(...) a syntax error.

A common typo, and hard to notice when it's silently ignored.

Fixes #284.
This commit is contained in:
whitequark 2020-01-31 06:37:45 +00:00
parent 3ac13eb8f9
commit 9964fc6b57
2 changed files with 43 additions and 4 deletions

View file

@ -1,6 +1,7 @@
from collections import OrderedDict, namedtuple from collections import OrderedDict, namedtuple
from collections.abc import Iterable from collections.abc import Iterable
from contextlib import contextmanager from contextlib import contextmanager, _GeneratorContextManager
from functools import wraps
from enum import Enum from enum import Enum
import warnings import warnings
@ -113,6 +114,27 @@ class _ModuleBuilderDomainSet:
self._builder._add_domain(domain) self._builder._add_domain(domain)
# It's not particularly clean to depend on an internal interface, but, unfortunately, __bool__
# must be defined on a class to be called during implicit conversion.
class _GuardedContextManager(_GeneratorContextManager):
def __init__(self, keyword, func, args, kwds):
self.keyword = keyword
return super().__init__(func, args, kwds)
def __bool__(self):
raise SyntaxError("`if m.{kw}(...):` does not work; use `with m.{kw}(...)`"
.format(kw=self.keyword))
def _guardedcontextmanager(keyword):
def decorator(func):
@wraps(func)
def helper(*args, **kwds):
return _GuardedContextManager(keyword, func, args, kwds)
return helper
return decorator
class FSM: class FSM:
def __init__(self, state, encoding, decoding): def __init__(self, state, encoding, decoding):
self.state = state self.state = state
@ -183,7 +205,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
SyntaxWarning, stacklevel=4) SyntaxWarning, stacklevel=4)
return cond return cond
@contextmanager @_guardedcontextmanager("If")
def If(self, cond): def If(self, cond):
self._check_context("If", context=None) self._check_context("If", context=None)
cond = self._check_signed_cond(cond) cond = self._check_signed_cond(cond)
@ -206,7 +228,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
self.domain._depth -= 1 self.domain._depth -= 1
self._statements = _outer_case self._statements = _outer_case
@contextmanager @_guardedcontextmanager("Elif")
def Elif(self, cond): def Elif(self, cond):
self._check_context("Elif", context=None) self._check_context("Elif", context=None)
cond = self._check_signed_cond(cond) cond = self._check_signed_cond(cond)
@ -226,7 +248,7 @@ class Module(_ModuleBuilderRoot, Elaboratable):
self.domain._depth -= 1 self.domain._depth -= 1
self._statements = _outer_case self._statements = _outer_case
@contextmanager @_guardedcontextmanager("Else")
def Else(self): def Else(self):
self._check_context("Else", context=None) self._check_context("Else", context=None)
src_loc = tracer.get_src_loc(src_loc_at=1) src_loc = tracer.get_src_loc(src_loc_at=1)

View file

@ -300,6 +300,23 @@ class DSLTestCase(FHDLTestCase):
with m.Elif(~True): with m.Elif(~True):
pass pass
def test_if_If_Elif_Else(self):
m = Module()
with self.assertRaises(SyntaxError,
msg="`if m.If(...):` does not work; use `with m.If(...)`"):
if m.If(0):
pass
with m.If(0):
pass
with self.assertRaises(SyntaxError,
msg="`if m.Elif(...):` does not work; use `with m.Elif(...)`"):
if m.Elif(0):
pass
with self.assertRaises(SyntaxError,
msg="`if m.Else(...):` does not work; use `with m.Else(...)`"):
if m.Else():
pass
def test_Switch(self): def test_Switch(self):
m = Module() m = Module()
with m.Switch(self.w1): with m.Switch(self.w1):