nandi/rustnimpublic Fork 0
87c9cc8
Commits
Clone
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Settle signed-shr and unsigned-wrap semantics against both compilers

Nim's shr on signed ints is arithmetic, and fixed-width unsigned arithmetic
wraps silently -- both matching Rust. Verified by running equivalent
programs under nim 2.2.4 and rustc 1.98.1 rather than by reading docs.

base16ct's constant-time decoder depends on the first, so it needs no
helper shim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-18T18:40:42-07:00 Browse files
87c9cc8 parent: 1a218c2
modified DESIGN.md +16 -8
@@ -75,14 +75,22 @@ Planned modules:
7575 - **Rust's expression-orientation** maps well: Nim `if`/`case` are expressions
7676 too, and a proc's trailing expression is its return value.
7777
78-### Open questions to settle empirically (the Nim toolchain is already here)
79-
80-1. Is Nim's `shr` on a **signed** integer arithmetic or logical? `base16ct`
81- needs arithmetic. Write the test before relying on either answer.
82-2. Rust debug builds **panic** on integer overflow; release builds wrap. Nim
83- raises `OverflowDefect` by default. Decide which Rust profile we model, say
84- so in the README, and map `wrapping_*`/`checked_*`/`saturating_*` explicitly.
85-3. `char`: Rust `char` is a Unicode scalar; mapped to `Rune`, which needs
78+### Settled empirically (Nim 2.2.4 vs rustc 1.98.1, both run)
79+
80+1. **Nim's `shr` on a signed integer is arithmetic**, matching Rust.
81+ `int16(-256) shr 8` = `-1` in Nim; `(-256i16) >> 8` = `-1` in Rust.
82+ `base16ct`'s decoder depends on this, so it maps directly with no helper.
83+2. **Nim's fixed-width unsigned arithmetic wraps silently**, matching Rust's
84+ `wrapping_*`. `uint8(200) + 100` = `44` in Nim; `200u8.wrapping_add(100)`
85+ = `44` in Rust. So `wrapping_add` on an unsigned type is just `+`.
86+
87+### Still open
88+
89+3. Rust debug builds **panic** on signed integer overflow; release builds wrap.
90+ Nim raises `OverflowDefect` on signed overflow. Item 2 settles the *unsigned*
91+ case only. Decide which Rust profile we model, state it in the README, and
92+ map `checked_*`/`saturating_*` explicitly.
93+4. `char`: Rust `char` is a Unicode scalar; mapped to `Rune`, which needs
8694 `std/unicode`. Confirm round-tripping.
8795
8896 ## Testing: differential, not golden
@@ -75,14 +75,22 @@ Planned modules:
75 - **Rust's expression-orientation** maps well: Nim `if`/`case` are expressions75 - **Rust's expression-orientation** maps well: Nim `if`/`case` are expressions
76 too, and a proc's trailing expression is its return value.76 too, and a proc's trailing expression is its return value.
77 77
78-### Open questions to settle empirically (the Nim toolchain is already here)78+### Settled empirically (Nim 2.2.4 vs rustc 1.98.1, both run)
79-79+
80-1. Is Nim's `shr` on a **signed** integer arithmetic or logical? `base16ct`80+1. **Nim's `shr` on a signed integer is arithmetic**, matching Rust.
81- needs arithmetic. Write the test before relying on either answer.81+ `int16(-256) shr 8` = `-1` in Nim; `(-256i16) >> 8` = `-1` in Rust.
82-2. Rust debug builds **panic** on integer overflow; release builds wrap. Nim82+ `base16ct`'s decoder depends on this, so it maps directly with no helper.
83- raises `OverflowDefect` by default. Decide which Rust profile we model, say83+2. **Nim's fixed-width unsigned arithmetic wraps silently**, matching Rust's
84- so in the README, and map `wrapping_*`/`checked_*`/`saturating_*` explicitly.84+ `wrapping_*`. `uint8(200) + 100` = `44` in Nim; `200u8.wrapping_add(100)`
85-3. `char`: Rust `char` is a Unicode scalar; mapped to `Rune`, which needs85+ = `44` in Rust. So `wrapping_add` on an unsigned type is just `+`.
86+
87+### Still open
88+
89+3. Rust debug builds **panic** on signed integer overflow; release builds wrap.
90+ Nim raises `OverflowDefect` on signed overflow. Item 2 settles the *unsigned*
91+ case only. Decide which Rust profile we model, state it in the README, and
92+ map `checked_*`/`saturating_*` explicitly.
93+4. `char`: Rust `char` is a Unicode scalar; mapped to `Rune`, which needs
86 `std/unicode`. Confirm round-tripping.94 `std/unicode`. Confirm round-tripping.
87 95
88 ## Testing: differential, not golden96 ## Testing: differential, not golden
modified src/main.rs +2 -3
@@ -1,3 +1,2 @@
1-fn main() {
2- println!("Hello, world!");
3-}
1+mod ty;
2+fn main() { println!("rustnim: scaffold"); }
@@ -1,3 +1,2 @@
1-fn main() {1+mod ty;
2- println!("Hello, world!");2+fn main() { println!("rustnim: scaffold"); }
3-}
modified src/ty.rs +0 -1
@@ -187,7 +187,6 @@ pub fn map(t: &Type) -> Result<Nim, String> {
187187
188188 fn discriminant(t: &Type) -> &'static str {
189189 match t {
190- Type::BareFn(_) => "bare fn",
191190 Type::Ptr(_) => "raw pointer",
192191 Type::TraitObject(_) => "trait object",
193192 Type::Never(_) => "never",
@@ -187,7 +187,6 @@ pub fn map(t: &Type) -> Result<Nim, String> {
187 187
188 fn discriminant(t: &Type) -> &'static str {188 fn discriminant(t: &Type) -> &'static str {
189 match t {189 match t {
190- Type::BareFn(_) => "bare fn",
191 Type::Ptr(_) => "raw pointer",190 Type::Ptr(_) => "raw pointer",
192 Type::TraitObject(_) => "trait object",191 Type::TraitObject(_) => "trait object",
193 Type::Never(_) => "never",192 Type::Never(_) => "never",