hdl._ast: enforce the ShapeCastable.const contract in Const().

This commit is contained in:
Wanda 2024-04-02 12:04:51 +02:00 committed by Catherine
parent 5577f4e703
commit c4bbcc6f8a
5 changed files with 18 additions and 11 deletions

View file

@ -1503,7 +1503,13 @@ class ValueLike(metaclass=_ValueLikeMeta):
class _ConstMeta(ABCMeta): class _ConstMeta(ABCMeta):
def __call__(cls, value, shape=None, src_loc_at=0, **kwargs): def __call__(cls, value, shape=None, src_loc_at=0, **kwargs):
if isinstance(shape, ShapeCastable): if isinstance(shape, ShapeCastable):
return shape.const(value) value = shape.const(value)
cast_shape = Shape.cast(shape)
cast_value = Const.cast(value)
if cast_value.shape() != cast_shape:
raise ValueError(f"Constant returned by {shape!r}.const() must have the shape that "
f"it casts to, {cast_shape!r}, and not {cast_value.shape()!r}")
return value
return super().__call__(value, shape, **kwargs, src_loc_at=src_loc_at + 1) return super().__call__(value, shape, **kwargs, src_loc_at=src_loc_at + 1)

View file

@ -223,12 +223,7 @@ class Layout(ShapeCastable, metaclass=ABCMeta):
field = self[key] field = self[key]
cast_field_shape = Shape.cast(field.shape) cast_field_shape = Shape.cast(field.shape)
if isinstance(field.shape, ShapeCastable): if isinstance(field.shape, ShapeCastable):
key_value = hdl.Const.cast(field.shape.const(key_value)) key_value = hdl.Const.cast(hdl.Const(key_value, field.shape))
if key_value.shape() != cast_field_shape:
raise ValueError("Constant returned by {!r}.const() must have the shape that "
"it casts to, {!r}, and not {!r}"
.format(field.shape, cast_field_shape,
key_value.shape()))
elif not isinstance(key_value, hdl.Const): elif not isinstance(key_value, hdl.Const):
key_value = hdl.Const(key_value, cast_field_shape) key_value = hdl.Const(key_value, cast_field_shape)
mask = ((1 << cast_field_shape.width) - 1) << field.offset mask = ((1 << cast_field_shape.width) - 1) << field.offset

View file

@ -83,7 +83,7 @@ class Memory(wiring.Component):
if isinstance(shape, ShapeCastable): if isinstance(shape, ShapeCastable):
self._elems = [None] * depth self._elems = [None] * depth
self._raw = [Const.cast(shape.const(None)).value] * depth self._raw = [Const.cast(Const(None, shape)).value] * depth
else: else:
self._elems = [0] * depth self._elems = [0] * depth
self._raw = self._elems # intentionally mutably aliased self._raw = self._elems # intentionally mutably aliased
@ -113,7 +113,7 @@ class Memory(wiring.Component):
self[actual_index] = actual_value self[actual_index] = actual_value
else: else:
if isinstance(self._shape, ShapeCastable): if isinstance(self._shape, ShapeCastable):
self._raw[index] = Const.cast(self._shape.const(value)).value self._raw[index] = Const.cast(Const(value, self._shape)).value
else: else:
value = operator.index(value) value = operator.index(value)
# self._raw[index] assigned by the following line # self._raw[index] assigned by the following line

View file

@ -139,7 +139,7 @@ class Member:
# TODO: We need a simpler way to check for "is this a valid constant initializer" # TODO: We need a simpler way to check for "is this a valid constant initializer"
if issubclass(type(self._description), ShapeCastable): if issubclass(type(self._description), ShapeCastable):
try: try:
self._init_as_const = Const.cast(self._description.const(self._init)) self._init_as_const = Const.cast(Const(self._init, self._description))
except Exception as e: except Exception as e:
raise TypeError(f"Port member initial value {self._init!r} is not a valid " raise TypeError(f"Port member initial value {self._init!r} is not a valid "
f"constant initializer for {self._description}") from e f"constant initializer for {self._description}") from e

View file

@ -522,10 +522,16 @@ class ConstTestCase(FHDLTestCase):
hash(Const(0)) hash(Const(0))
def test_shape_castable(self): def test_shape_castable(self):
class MockConstValue: class MockConstValue(ValueCastable):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
def shape(self):
return MockConstShape()
def as_value(self):
return Const(self.value, 8)
class MockConstShape(ShapeCastable): class MockConstShape(ShapeCastable):
def as_shape(self): def as_shape(self):
return unsigned(8) return unsigned(8)