Skip to content

Support integer-to-integer casts - #278

Merged
coord-e merged 2 commits into
mainfrom
claude/vibrant-brown-tjmb6h
Sep 24, 2026
Merged

coord-e merged 2 commits into
mainfrom
claude/vibrant-brown-tjmb6h

Conversation

@coord-e

@coord-e coord-e commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Rvalue::Cast(IntToInt) fell through to the catch-all unimplemented!("rvalue=..."). So code like impl Idx for u32 { fn new(idx: usize) -> u32 { idx as u32 } fn index(self) -> usize { self as usize } } could only be handled with #[thrust::trusted].

Changes

  • An integer as cast never panics. Instead it truncates or reinterprets the value without warning, and the refinement now models that:
    • When the target type's range contains the source type's range (e.g. u32 → usize, usize → u128, u32 → i64), the value passes through unchanged.
    • Otherwise the value is wrapped into the target type's range:
      • unsigned N-bit target: x mod 2^N
      • signed N-bit target: (x + 2^(N-1)) mod 2^N - 2^(N-1)
  • Adds Function::MOD / Term::mod_ and Term::pow2 to chc. Term::Int holds an i64, so pow2 builds 2^64 and larger as a product of smaller powers.
  • Casts involving bool or char are also IntToInt in MIR. They still reach unimplemented!.

Tests

  • int_cast: the Idx example above, without #[thrust::trusted]
  • int_cast_truncate: (2^32 + 5) as u32 == 5, via u64 → u32
  • int_cast_signed: 4294967295 as i32 == -1, via u32 → i32

If every cast is forced to identity, the pass sides of int_cast_truncate and int_cast_signed both fail.

Known limitations

  • i8/u8/i16/u16/i128/u128 have no Model impl in std.rs, so functions taking or returning them still panic in template building.
  • Arithmetic overflow under -C debug-assertions=off is not modeled. A value can therefore fall outside its type's range, and a cast that passes the value through unchanged inherits that gap.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MvvhC4sHsobSMHAn9CgLBq


Generated by Claude Code

Rvalue::Cast(IntToInt) fell through to the catch-all unimplemented!, so
code such as `impl Idx for u32 { fn new(idx: usize) -> u32 { idx as u32 } }`
could not be verified without #[thrust::trusted].

An `as` cast between integer types never panics; it silently truncates or
reinterprets the value. The cast is therefore identity only when the
source type's range lies inside the target's; otherwise the result is the
value wrapped into the target's range with SMT-LIB `mod`. Casts involving
bool or char, which MIR also represents as IntToInt, still reach
unimplemented!.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MvvhC4sHsobSMHAn9CgLBq

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The recursive large-power and negative-dividend wrapping paths lack the repository-required paired UI coverage.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

Adds refinement support for integer-to-integer casts, including truncation and signed reinterpretation.

Changes:

  • Models integer casts using identity or modular wrapping.
  • Adds SMT mod and large power-of-two terms.
  • Adds paired UI tests for identity, truncating, and signed casts.
File Description
src/​analyze/​basic_block.rs Implements integer-cast refinement.
src/​chc.rs Adds modulo and power-of-two terms.
tests/​ui/​pass/​int_cast.rs Tests valid Idx casts.
tests/​ui/​fail/​int_cast.rs Tests rejection of an invalid cast assertion.
tests/​ui/​pass/​int_cast_truncate.rs Tests valid truncation.
tests/​ui/​fail/​int_cast_truncate.rs Tests incorrect truncation output.
tests/​ui/​pass/​int_cast_signed.rs Tests valid signed reinterpretation.
tests/​ui/​fail/​int_cast_signed.rs Tests incorrect signed output.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/chc.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MvvhC4sHsobSMHAn9CgLBq
@coord-e
coord-e marked this pull request as ready for review September 24, 2026 13:09
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-24T13:16:08.073784Z 4704078 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 47040781bd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +731 to +732
if int_ty_includes(self.tcx, ty, op_ty) {
op_pty

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Normalize the source value before widening casts

When the operand comes from overflowing integer arithmetic with debug assertions disabled, its logical term can be outside the source type's range because BinaryOp::Add is modeled as unbounded integer addition. Returning it unchanged makes verification unsound: fn f(x: u32) -> u64 { (x + 1) as u64 } followed by assert!(f(u32::MAX) == 4294967296) is accepted by the generated constraints, although Rust wraps the addition and returns 0. Normalize the term to the source type's range before deciding that a widening cast is an identity.

Useful? React with 👍 / 👎.

@coord-e
coord-e merged commit ff4f51a into main Sep 24, 2026
6 checks passed
@coord-e
coord-e deleted the claude/vibrant-brown-tjmb6h branch September 24, 2026 13:20
coeff-aij added a commit to coeff-aij/thrust that referenced this pull request Sep 24, 2026
…casts) into forall-sort

Brings in coord-e#114 (Rust expressions as thrust::predicate bodies,
instantiated per generic arguments), coord-e#275 (newer PCSat in CI), coord-e#276, coord-e#277
and coord-e#278 on top of forall-sort.

Resolution:
- A predicate call whose instance resolves goes through
  Analyzer::predicate_with_args, so a Rust-body predicate is defined once
  per instantiation and a raw SMT-LIB2 predicate keeps its single
  definition. A call that does not resolve (it still depends on the
  owner's type parameters) keeps the forall predicate.
- predicate_with_args takes the calling function as owner, since
  forall-sort translates type parameters relative to it; an instance
  whose arguments mention type parameters is keyed by that owner too.
- UserDefinedPredDef keeps both the new body enum and the ForallPred
  dependency set. A formula body contributes its ForallPred atoms
  directly and its calls to other user-defined predicates as edges of
  the dependency graph; a raw body is still scanned by name.
- The refinement clause builder records the value-variable origin only
  when the value term is a variable, as the singleton case substitutes
  a default term.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants