Skip to content

Support Rust syntax in thrust::predicate bodies#114

Draft
coord-e wants to merge 5 commits into
mainfrom
claude/tender-ramanujan-F2ISx
Draft

Support Rust syntax in thrust::predicate bodies#114
coord-e wants to merge 5 commits into
mainfrom
claude/tender-ramanujan-F2ISx

Conversation

@coord-e

@coord-e coord-e commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Summary

#[thrust::predicate] bodies can now be written as ordinary Rust boolean expressions instead of raw SMT-LIB2 string literals, reusing the existing formula_fn translation pipeline (AnnotFnTranslator).

// before — raw SMT2
#[thrust_macros::predicate]
fn is_double(self, doubled: Self) -> bool {
    "(= (* (tuple_proj<Int>.0 self_) 2) (tuple_proj<Int>.0 doubled))"; true
}

// after — Rust syntax
#[thrust_macros::predicate]
fn is_double(self, doubled: Self) -> bool {
    self.x * 2 == doubled.x
}

Raw SMT2 string bodies still work unchanged (backward compatible).

How it works

A predicate written with #[thrust_macros::predicate] whose body is a Rust expression is expanded with an additional #[thrust::formula_fn] attribute, which is the single discriminator:

  • present ⇒ the body is interpreted by AnnotFnTranslator through the existing formula_fns cache (formula_fn_with_args), exactly like requires/ensures/invariant. The resulting chc::Formula is emitted as the predicate's SMT define-fun.
  • absent ⇒ the legacy raw-SMT2 string path (unchanged).

The macro picks which by inspecting the body (string-literal statement ⇒ raw; otherwise Rust). Borrow-check skipping is free, since mir_borrowck_skip_formula_fn already keys on the formula_fn attribute. Predicate call sites are still resolved as named UserDefinedPred atoms, so the dual attribute doesn't change call-site behavior.

Changes

  • chc.rsUserDefinedPredDef body becomes a UserDefinedPredBody { Raw, Formula } enum; add push_pred_define_formula. New TermSortEnv trait (var_sort/term_sort), implemented for Clause and IndexVec<TermVarIdx, Sort>.
  • chc/smtlib2.rs — the Term/Atom/Formula/Body Display wrappers now hold &dyn TermSortEnv instead of &Clause. A Formula predicate body is rendered by passing the sig's sorts as an IndexVecno synthetic/fake Clause is fabricated.
  • chc/format_context.rs — uses the TermSortEnv trait (moved Clause::term_sort onto it).
  • chc/unbox.rs — unbox Formula predicate bodies.
  • analyze/crate_.rs — register predicate items also marked formula_fn.
  • analyze/local_def.rsdefine_as_predicate branches on the formula_fn attribute; pulls the formula from formula_fn_with_args, naming params v{i}.
  • thrust-macros/src/spec.rsexpand_predicate: detect raw-vs-Rust body; for Rust bodies add #[thrust::formula_fn], rewrite selfself_, add allow-attrs.
  • tests — scalar (annot_preds), trait (annot_preds_trait), and multi-field (annot_preds_trait_multi) predicate tests (pass + fail) converted to Rust syntax.

Dependency

Named struct-field access (self.x) in the trait/multi-field predicates relies on #118 (merged), which taught the formula translator to resolve named ADT fields. main is merged into this branch, so that support is included here.

Verification

  • Inspected the generated SMT2: self.x * 2 == doubled.x lowers to exactly (= (* (tuple_proj<Int>.0 v0) 2) (tuple_proj<Int>.0 v1)) — identical to the previous raw form.
  • Full ui suite: 230 passed (z3 4.13.0 + docker pcsat for the relevant tests).

https://claude.ai/code/session_01WdLyxyy4ieAxrexj83X5MX

claude added 2 commits June 8, 2026 17:03
Predicate bodies can now be written as ordinary Rust boolean expressions
instead of raw SMT-LIB2 string literals, reusing the formula_fn translation
pipeline (AnnotFnTranslator).

A predicate whose body is a Rust expression is emitted by the
thrust_macros::predicate proc macro with an additional #[thrust::formula_fn]
attribute; that attribute is the discriminator. When present, the body is
translated through the existing formula_fns cache and the resulting
chc::Formula is emitted as the predicate's SMT define-fun. Raw SMT2 string
bodies continue to work unchanged.

- chc: UserDefinedPredDef gains a UserDefinedPredBody { Raw, Formula } enum
  plus push_pred_define_formula; smtlib2 renders a Formula body via a
  synthetic Clause; unbox unboxes Formula bodies.
- analyze: register predicate items marked formula_fn; define_as_predicate
  pulls the translated formula from formula_fn_with_args and names params
  v{i} to match TermVarIdx rendering.
- annot_fn: ExprKind::Field resolves named ADT fields (e.g. self.x) to tuple
  projection indices, enabling struct predicates in trait impls.
- macro: detect raw-vs-Rust body, rewrite self->self_, add allow attrs.
- tests: paired pass/fail ui tests for free-function and trait predicates.

https://claude.ai/code/session_01WdLyxyy4ieAxrexj83X5MX
Rewrite the existing predicate ui tests (annot_preds, annot_preds_trait,
annot_preds_trait_multi; pass and fail) to use the new Rust-expression
predicate bodies instead of raw SMT-LIB2 string literals, and drop the
now-redundant *_rust_syntax duplicate tests added earlier.

https://claude.ai/code/session_01WdLyxyy4ieAxrexj83X5MX
claude added 3 commits June 11, 2026 01:50
Replace the synthetic `Clause` used to render a predicate's translated
formula with a `TermSortEnv` trait: anything that can resolve a term-level
variable's sort (a `Clause` via its `vars`, or a bare `IndexVec<TermVarIdx,
Sort>` for a define-fun signature) can drive term rendering. The smtlib2
Display wrappers now hold `&dyn TermSortEnv` instead of `&Clause`, so no
fake clause is fabricated.

Also revert the named-struct-field translator change and the trait
predicate test conversions: that field resolution is now a separate PR
(#118). The trait predicate tests stay on raw SMT2 here and can move to
Rust syntax once the field PR lands; the scalar free-function predicate
test exercises the Rust-syntax path.

https://claude.ai/code/session_01WdLyxyy4ieAxrexj83X5MX
Now that named struct-field resolution is on main (#118), flip the trait
predicate tests (annot_preds_trait, annot_preds_trait_multi; pass and fail)
from raw SMT-LIB2 bodies to Rust-expression bodies using `self.x`.

https://claude.ai/code/session_01WdLyxyy4ieAxrexj83X5MX
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.

2 participants