Repository: amaranth-lang/amaranth
Pull request #897 (2023-09-03 04:12:11)

ast: ensure `Part` offset is unsigned
Related to #807.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/897?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) Report
> Merging [#897](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/897?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) (8b61cbd) into [main](https://app.codecov.io/gh/amaranth-lang/amaranth/commit/8c4a15ab92af10d5640be4d15f27f0e8843ec154?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) (8c4a15a) will **decrease** coverage by `0.40%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main     #897      +/-   ##
==========================================
- Coverage   83.89%   83.50%   -0.40%     
==========================================
  Files          54       54              
  Lines        7613     7608       -5     
  Branches     1844     1535     -309     
==========================================
- Hits         6387     6353      -34     
- Misses       1029     1060      +31     
+ Partials      197      195       -2     
```


| [Files Changed](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/897?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang) | Coverage Δ | |
|---|---|---|
| [amaranth/hdl/ast.py](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/897?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang#diff-YW1hcmFudGgvaGRsL2FzdC5weQ==) | `89.90% <100.00%> (+0.02%)` | :arrow_up: |

... and [10 files with indirect coverage changes](https://app.codecov.io/gh/amaranth-lang/amaranth/pull/897/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang)

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=amaranth-lang)


Patch:
diff --git a/amaranth/hdl/ast.py b/amaranth/hdl/ast.py
index e1ee694..3fdb031 100644
--- a/amaranth/hdl/ast.py
+++ b/amaranth/hdl/ast.py
@@ -860,9 +860,14 @@ class Part(Value):
         if not isinstance(stride, int) or stride <= 0:
             raise TypeError("Part stride must be a positive integer, not {!r}".format(stride))
 
+        value = Value.cast(value)
+        offset = Value.cast(offset)
+        if offset.shape().signed:
+            raise TypeError("Part offset must be unsigned")
+
         super().__init__(src_loc_at=src_loc_at)
-        self.value  = Value.cast(value)
-        self.offset = Value.cast(offset)
+        self.value  = value
+        self.offset = offset
         self.width  = width
         self.stride = stride
 


Test patch:
diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py
index 0cb8865..760744c 100644
--- a/tests/test_hdl_ast.py
+++ b/tests/test_hdl_ast.py
@@ -785,6 +785,11 @@ class BitSelectTestCase(FHDLTestCase):
         s = self.c.bit_select(self.s, 2)
         self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 1)")
 
+    def test_offset_wrong(self):
+        with self.assertRaisesRegex(TypeError,
+                r"^Part offset must be unsigned$"):
+            self.c.bit_select(self.s.as_signed(), 1)
+
 
 class WordSelectTestCase(FHDLTestCase):
     def setUp(self):
@@ -817,6 +822,11 @@ class WordSelectTestCase(FHDLTestCase):
         s = self.c.word_select(self.s, 2)
         self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 2)")
 
+    def test_offset_wrong(self):
+        with self.assertRaisesRegex(TypeError,
+                r"^Part offset must be unsigned$"):
+            self.c.word_select(self.s.as_signed(), 1)
+
 
 class CatTestCase(FHDLTestCase):
     def test_shape(self):