diff --git a/pyVHDLModel/Symbol.py b/pyVHDLModel/Symbol.py index d39e9e4e1..0e2c3cc96 100644 --- a/pyVHDLModel/Symbol.py +++ b/pyVHDLModel/Symbol.py @@ -879,22 +879,22 @@ class ScalarConstraint(Constraint, mixin=True): * :class:`Constrained scalar subtype symbol ` """ - _constraint: Nullable[Range] #: The range constraining the scalar subtype, or ``None`` if unconstrained. + _constraint: Range #: The range constraining the scalar subtype. - def __init__(self, constraint: Nullable[Range]) -> None: + def __init__(self, constraint: Range) -> None: """ Initializes a scalar constraint. - :param constraint: The range constraining the scalar subtype, or ``None`` if unconstrained. + :param constraint: The range constraining the scalar subtype. """ self._constraint = constraint @readonly - def Constraint(self) -> Nullable[Range]: + def Constraint(self) -> Range: """ Read-only property to access the scalar type's range constraint (:attr:`_constraint`). - :returns: The constraint, or ``None`` if not set. + :returns: The constraint of the scalar subtype. """ return self._constraint @@ -904,7 +904,8 @@ class ConstrainedScalarSubtypeSymbol(SubtypeSymbol, ScalarConstraint): """ Represents a reference to a scalar subtype narrowed by a range. - The referenced language entity is available as :data:`Reference` once resolved. + The referenced language entity is available as :data:`Reference` once resolved. The range is + mandatory: a type mark without a range constraint is a :class:`~pyVHDLModel.Symbol.SimpleSubtypeSymbol`. .. admonition:: Example @@ -913,14 +914,23 @@ class ConstrainedScalarSubtypeSymbol(SubtypeSymbol, ScalarConstraint): for i in integer range 0 to 3 loop -- ^^^^^^^ <- Name -- ^^^^^^ <- Constraint + + A range constraint written as a range attribute is a :class:`~pyVHDLModel.Base.RangeFromName` + referring to a :class:`~pyVHDLModel.Symbol.RangeAttributeSymbol`: + + .. code-block:: VHDL + + subtype index is natural range vector'range; + -- ^^^^^^^ <- Name + -- ^^^^^^^^^^^^ <- Constraint """ - def __init__(self, name: Name, constraint: Nullable[Range] = None) -> None: + def __init__(self, name: Name, constraint: Range) -> None: """ Initializes a reference to a scalar subtype narrowed by a range. :param name: The name to reference the language entity. - :param constraint: The range constraining the scalar subtype, or ``None`` if unconstrained. + :param constraint: The range constraining the scalar subtype. """ super().__init__(name) ScalarConstraint.__init__(self, constraint) diff --git a/tests/unit/Instantiation/Model.py b/tests/unit/Instantiation/Model.py index fd4d0efca..127104d19 100644 --- a/tests/unit/Instantiation/Model.py +++ b/tests/unit/Instantiation/Model.py @@ -365,9 +365,9 @@ def test_ConstrainedScalarSubtypeSymbol(self) -> None: self.assertIs(rng, symbol.Constraint) def test_ConstrainedScalarSubtypeSymbol_withoutConstraint(self) -> None: - symbol = ConstrainedScalarSubtypeSymbol(SimpleName("integer")) - - self.assertIsNone(symbol.Constraint) + """The range constraint is mandatory - ``integer range`` isn't a VHDL construct.""" + with self.assertRaises(TypeError): + ConstrainedScalarSubtypeSymbol(SimpleName("integer")) class PSLEntities(TestCase): diff --git a/tests/unit/Instantiation/Symbol.py b/tests/unit/Instantiation/Symbol.py index eacd55e68..8aa3279d1 100644 --- a/tests/unit/Instantiation/Symbol.py +++ b/tests/unit/Instantiation/Symbol.py @@ -39,8 +39,8 @@ """ from unittest import TestCase -from pyVHDLModel.Base import Direction, SimpleRange -from pyVHDLModel.Name import SimpleName, AllName +from pyVHDLModel.Base import Direction, SimpleRange, RangeFromName +from pyVHDLModel.Name import SimpleName, AllName, AttributeName from pyVHDLModel.Expression import IntegerLiteral from pyVHDLModel.Symbol import ( PossibleReference, Symbol, @@ -49,7 +49,7 @@ PackageMemberReferenceSymbol, AllPackageMembersReferenceSymbol, EntityInstantiationSymbol, ComponentInstantiationSymbol, ConfigurationInstantiationSymbol, EntitySymbol, ArchitectureSymbol, PackageSymbol, - RecordElementSymbol, SubtypeSymbol, SimpleSubtypeSymbol, + RangeAttributeSymbol, RecordElementSymbol, SubtypeSymbol, SimpleSubtypeSymbol, ConstrainedScalarSubtypeSymbol, ConstrainedArraySubtypeSymbol, ConstrainedRecordSubtypeSymbol, SimpleObjectOrFunctionCallSymbol, IndexedObjectOrFunctionCallSymbol, ) @@ -203,13 +203,19 @@ def test_ScalarConstraint_WithRange(self) -> None: self.assertIs(constraint, symbol.Constraint) - def test_ScalarConstraint_WithoutRange(self) -> None: - """``None`` only means the range constraint was written as an attribute name - (``subtype s is t'range;``), which isn't implemented yet - not that the source omitted a - constraint (it never does for a constrained scalar subtype). See ``Constraint``'s docstring.""" - symbol = ConstrainedScalarSubtypeSymbol(SimpleName("integer")) + def test_ScalarConstraint_RangeIsMandatory(self) -> None: + """``integer range`` isn't a VHDL construct - a type mark without a range constraint is a + ``SimpleSubtypeSymbol``, so the range can't be omitted here.""" + with self.assertRaises(TypeError): + ConstrainedScalarSubtypeSymbol(SimpleName("integer")) - self.assertIsNone(symbol.Constraint) + def test_ScalarConstraint_WithRangeAttribute(self) -> None: + """``subtype index is natural range vector'range;`` - the range constraint is a range attribute, + which is a range denoted by a name.""" + constraint = RangeFromName(RangeAttributeSymbol(AttributeName("range", SimpleName("vector")))) + symbol = ConstrainedScalarSubtypeSymbol(SimpleName("natural"), constraint) + + self.assertIs(constraint, symbol.Constraint) def test_ArrayConstraint(self) -> None: constraint = SimpleRange(IntegerLiteral(7), IntegerLiteral(0), Direction.DownTo)