Skip to content

Conversation

@onbjerg
Copy link
Contributor

@onbjerg onbjerg commented Dec 17, 2025

When both operands of a binary operation are IntLiteral, evaluate the expression at compile time to preserve literal type semantics. This allows expressions like -(1 + 2) to work correctly, since the result remains an IntLiteral that can be negated.

Changes:

  • Extended IntScalar with explicit sign tracking and signed arithmetic
  • Added try_eval_int_literal_binop to evaluate literal binary expressions
  • Updated eval_array_len to reject negative array lengths

On top of #648

Stack:

@onbjerg onbjerg requested a review from DaniPopes as a code owner December 17, 2025 00:57
@onbjerg onbjerg added C-enhancement Category: an issue proposing an enhancement or a PR with one A-sema Area: semantic analysis labels Dec 17, 2025
BitXor => Self::new(self.data ^ r.data),
Shr => Self::new(self.data.wrapping_shr(r.data.try_into().unwrap_or(usize::MAX))),
Shl => Self::new(self.data.wrapping_shl(r.data.try_into().unwrap_or(usize::MAX))),
Sar => Self::new(self.data.arithmetic_shr(r.data.try_into().unwrap_or(usize::MAX))),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is >>> which only works on fixed types, we should rename this to sometihng so we dont forget its useless

BitOr => Self::new(self.data | r.data),
BitAnd => Self::new(self.data & r.data),
BitXor => Self::new(self.data ^ r.data),
Shr => Self::new(self.data.wrapping_shr(r.data.try_into().unwrap_or(usize::MAX))),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be arithmetic_shr depending on sign

}

/// Returns whether the value is negative.
pub fn is_negative(&self) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably be is_signed and be a constant value. then data should be I256 and is_negative will be I256's is_negative (last bit set). this way all ops are easier to keep track of, since they become regular two's complement

DaniPopes pushed a commit that referenced this pull request Dec 22, 2025
This enables distinguishing edge cases like 127 (fits in int8) from 128
(doesn't fit), which is needed for correct implicit and explicit integer
conversions.

- Changed internal storage from `u8` (bytes 0-32) to `u16` (bits 0-256)
- `TypeSize::new()` now takes bits directly
- Added `new_fb_bytes()` for fixed-bytes types that take byte counts
- `bits()` returns stored value, `bytes()` computes `ceil(bits/8)`
- `mk_ty_int_literal` no longer rounds to multiples of 8

Closes #650

Unblocks #647 #648 #649
@onbjerg onbjerg force-pushed the onbjerg/int-literal-negative branch from addc964 to e4c688d Compare December 22, 2025 19:12
@onbjerg onbjerg force-pushed the onbjerg/int-literal-const-fold branch from d3a3036 to a1f3e12 Compare December 22, 2025 19:12
@onbjerg onbjerg force-pushed the onbjerg/int-literal-negative branch from e4c688d to d621ddf Compare December 22, 2025 19:13
@onbjerg onbjerg force-pushed the onbjerg/int-literal-const-fold branch from a1f3e12 to 9ee451e Compare December 22, 2025 19:13
onbjerg added a commit that referenced this pull request Dec 22, 2025
Add implicit type coercion for integer literals (IntLiteral) to typed
integers (uint/int). The coercion rules are:

- IntLiteral -> uint: Allowed if the literal is non-negative and fits in
the target size (size.bits() <= target.bits())

- IntLiteral -> int: Allowed with strict inequality (size.bits() <
target.bits()) for non-negative values due to TypeSize rounding, and
non-strict for negative values

TypeSize stores ceil(bit_len/8), so int_literal[1] covers 0-255. This
means we can't distinguish edge cases like 127 (fits in int8) from 128
(doesn't fit), so we conservatively require int16+ for int_literal[1].

Note: Negative literal support requires additional work in the type
checker to propagate negativity through unary negation. This is provided
in a follow up.

Supercedes #564 and closes #627
(closes #564)

Stack:

- #647 (this)
- #648 
- #649
@onbjerg onbjerg force-pushed the onbjerg/int-literal-negative branch from d621ddf to 6860eb9 Compare December 22, 2025 19:36
@onbjerg onbjerg force-pushed the onbjerg/int-literal-const-fold branch from 9ee451e to 44ebc93 Compare December 22, 2025 19:36
Base automatically changed from onbjerg/int-literal-negative to main December 22, 2025 19:39
onbjerg added a commit that referenced this pull request Dec 22, 2025
Implement proper handling of negative integer literals in the type
checker. Previously, negative literals like -42 were broken because:

1. The parser represents them as unary negation applied to a positive
literal, so type_of_lit only sees the positive value
2. The negativity flag in IntLiteral was always false

This commit fixes the issue by:

- Allowing unary negation on IntLiteral types (they can always be
negated since the result is just a negative literal)
- Propagating the negativity flag when applying unary negation to an
IntLiteral, flipping neg from false to true
- Not propagating the expected type through negation when targeting
signed types, to avoid premature type mismatch errors on the inner
expression

Also simplifies the coercion rule to use strict inequality for both
positive and negative literals, since TypeSize rounding means we can't
reliably distinguish edge cases in either direction.

On top of #647 

Supercedes #566 (will mark @mablr as a co-author to commend effort) and
closes #560 (closes #566)

Stack:

- #647
- #648 (this)
- #649

Co-Authored-By: Mablr <[email protected]>
Implement proper handling of negative integer literals in the type
checker. Previously, negative literals like -42 were broken because:

1. The parser represents them as unary negation applied to a positive
   literal, so type_of_lit only sees the positive value
2. The negativity flag in IntLiteral was always false

This commit fixes the issue by:

- Allowing unary negation on IntLiteral types (they can always be
  negated since the result is just a negative literal)
- Propagating the negativity flag when applying unary negation to an
  IntLiteral, flipping neg from false to true
- Not propagating the expected type through negation when targeting
  signed types, to avoid premature type mismatch errors on the inner
  expression

Also simplifies the coercion rule to use strict inequality for both
positive and negative literals, since TypeSize rounding means we can't
reliably distinguish edge cases in either direction.
…servation

When both operands of a binary operation are IntLiteral, evaluate the
expression at compile time to preserve literal type semantics. This
allows expressions like `-(1 + 2)` to work correctly, since the result
remains an IntLiteral that can be negated.

Changes:
- Extended IntScalar with explicit sign tracking and signed arithmetic
- Added try_eval_int_literal_binop to evaluate literal binary expressions
- Updated eval_array_len to reject negative array lengths
@onbjerg onbjerg force-pushed the onbjerg/int-literal-const-fold branch from 44ebc93 to 522c0bb Compare December 22, 2025 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-sema Area: semantic analysis C-enhancement Category: an issue proposing an enhancement or a PR with one

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants