Type Widening
When an arithmetic or comparison expression mixes numeric types, Cambrian widens both operands to a common type that can represent every value of either operand. No explicit cast is required for ordinary same-sign mixes.
Rules
Rule 1 — Same sign
Result width is max(width); signedness is unchanged:
m_total: u128 + amount: u64 // amount widened to u128
m_x: i32 + m_y: i64 // m_x widened to i64
Rule 2 — Mixed signedness
Result is signed with
width = max(2 × unsigned_width, signed_width):
m_count: u32 + delta: i8 // both → i64
m_val: u64 + offset: i64 // both → i128
Doubling the unsigned width keeps the full unsigned range representable in the signed result.
Mixed-sign arithmetic lowers on both backends in member transforms and
pure fn bodies. On EVM, a mixed-sign expression directly in a route body
(a let value or return(...)) does not yet compile; compute it in a
pure fn and call that from the route.
Rule 3 — u128 / U256 mixed with signed
There is no wider signed primitive than i128, so mixing u128 or U256
with any signed type in arithmetic has no common type and is rejected.
Cast one side explicitly with as. (Comparisons are exact and allowed for
u128; only U256 against a signed operand is rejected there.)
m_a: u128 + d: i8 // rejected
m_b: U256 + d: i32 // rejected
Any unsigned type combined with U256 widens to U256. Integer literals
adapt automatically: m_balance + 1 with m_balance: U256 treats 1 as
U256.
Full widening table
Cell at row A, column B is the result of A op B. Symmetric cells are
omitted; ERR marks a rejected arithmetic combination (Rule 3).
| u8 | u16 | u32 | u64 | u128 | U256 | i8 | i16 | i32 | i64 | i128 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| u8 | u8 | u16 | u32 | u64 | u128 | U256 | i16 | i16 | i32 | i64 | i128 |
| u16 | u16 | u32 | u64 | u128 | U256 | i32 | i32 | i32 | i64 | i128 | |
| u32 | u32 | u64 | u128 | U256 | i64 | i64 | i64 | i64 | i128 | ||
| u64 | u64 | u128 | U256 | i128 | i128 | i128 | i128 | i128 | |||
| u128 | u128 | U256 | ERR | ERR | ERR | ERR | ERR | ||||
| U256 | U256 | ERR | ERR | ERR | ERR | ERR | |||||
| i8 | i8 | i16 | i32 | i64 | i128 | ||||||
| i16 | i16 | i32 | i64 | i128 | |||||||
| i32 | i32 | i64 | i128 | ||||||||
| i64 | i64 | i128 | |||||||||
| i128 | i128 |
Practical guidance
- Prefer same-sign arithmetic so widening stays obvious.
- Mixing
u64with signed values yieldsi128— larger than most balance fields. On EVM, keep mixed-sign expressions out of route bodies (see Rule 2). - Do not mix
u128/U256with signed types; cast one side first. - Widening applies to checked and wrapping operators alike; overflow checking (or wrap) happens on the result type after widening.