Skip to content

Support quantifiers in cvc5 and bitwuzla (#662) - #669

Open
filipeom wants to merge 1 commit into
mainfrom
filip/issue-662
Open

Support quantifiers in cvc5 and bitwuzla (#662)#669
filipeom wants to merge 1 commit into
mainfrom
filip/issue-662

Conversation

@filipeom

Copy link
Copy Markdown
Member

Closes #662

@filipeom
filipeom requested a review from a team as a code owner July 29, 2026 19:32
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@filipeom, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f24be626-15f2-4422-a968-2228801317a6

📥 Commits

Reviewing files that changed from the base of the PR and between 7f327ab and 4d1896a.

📒 Files selected for processing (23)
  • README.md
  • docs/examples.mld
  • docs/examples/product_mix.ml
  • docs/index.mld
  • src/smtml/bitwuzla_mappings.default.ml
  • src/smtml/cvc5_mappings.default.ml
  • src/smtml/dolmenexpr_to_expr.ml
  • src/smtml/dolmenexpr_to_expr.mli
  • src/smtml/mappings.ml
  • src/smtml/mappings_intf.ml
  • src/smtml/parser.mly
  • src/smtml/smtlib.ml
  • src/smtml/symbol.ml
  • src/smtml/typed.ml
  • src/smtml/typed.mli
  • src/smtml/z3_mappings.default.ml
  • test/integration/test_solver.ml
  • test/regression/dune
  • test/regression/test_issue_662.ml
  • test/test_harness.ml
  • test/unit/test_expr.ml
  • test/unit/test_model.ml
  • test/unit/test_smtlib_pp.ml
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added support for creating symbolic variables across SMT solver backends.
    • Added universal and existential quantifier construction for boolean expressions.
    • Added clearer separation between constants and variables in symbolic expressions.
  • Bug Fixes

    • Quantified formulas now work with CVC5 and Bitwuzla instead of failing as unsupported.
  • Tests

    • Added regression coverage for existential and universal bitvector formulas across multiple solvers.

Walkthrough

The change adds explicit constant and variable constructors, namespace-aware encoding, and quantified expression support. CVC5 and Bitwuzla now construct forall and exists terms. Regression and existing tests use the updated symbol APIs.

Changes

Quantified term support

Layer / File(s) Summary
Variable and quantifier contracts
src/smtml/symbol.*, src/smtml/mappings_intf.ml, src/smtml/dolmenexpr_to_expr.*, src/smtml/typed.*, src/smtml/mappings.nop.ml
Adds explicit constant and variable constructors and exposes typed Boolean forall and exists helpers.
Backend variable and quantifier construction
src/smtml/*_mappings.default.ml
Adds backend variable constructors and implements CVC5 and Bitwuzla quantifier construction.
Variable-aware symbol encoding
src/smtml/mappings.ml, src/smtml/parser.mly, src/smtml/model.ml, src/smtml/ast.ml, src/smtml/altergo_mappings.default.ml
Routes variable-namespaced symbols to cached variable terms and updates constant symbol construction.
Quantifier regression and API migration
test/regression/*, test/integration/test_solver.ml, test/unit/*, test/test_harness.ml, docs/examples/product_mix.ml
Adds quantified bitvector checks across CVC5, Bitwuzla, and Z3 and updates existing callers to use explicit constant construction.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Suggested reviewers: hra687261

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: quantifier support for CVC5 and Bitwuzla.
Description check ✅ Passed The description links the pull request to issue #662, which covers the implemented quantifier support.
Linked Issues check ✅ Passed The changes implement existential quantifiers for CVC5 and Bitwuzla and add variable encoding needed for bound expressions in issue #662.
Out of Scope Changes check ✅ Passed The variable, symbol, forall, parser, model, and regression-test changes directly support quantifier encoding and related API updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch filip/issue-662

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/smtml/dolmenexpr_to_expr.mli (1)

63-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Complete odoc documentation for the new quantifier APIs.

Both interface additions expose public functions without the required parameter and return documentation.

  • src/smtml/dolmenexpr_to_expr.mli#L63-L63: document var with @param and @return tags.
  • src/smtml/typed.mli#L142-L147: document vars, e, and the return value for forall and exists.

As per path instructions, interface files must fully document public functions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/smtml/dolmenexpr_to_expr.mli` at line 63, The public quantifier APIs lack
complete odoc parameter and return documentation. In
src/smtml/dolmenexpr_to_expr.mli lines 63-63, document var’s string and ty
parameters and returned term; in src/smtml/typed.mli lines 142-147, document
vars and e plus the return values of forall and exists, without changing their
signatures or behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/regression/test_issue_662.ml`:
- Line 1: Add the repository-standard SPDX, copyright, and author header to
test_issue_662.ml, followed by a top-level (** ... *) module documentation
comment before the existing Smtml.Typed.Bitv32.symbol declaration.
- Around line 12-16: Update both backend result matches in test_issue_662.ml to
succeed only for Sat and fail the regression for Unsat or Unknown. Extend the
regression with a universally quantified unsatisfiable case that ensures the
tested variable is actually bound, and assert that both backends return Sat for
the intended cases while preserving explicit failure for unexpected results.

---

Nitpick comments:
In `@src/smtml/dolmenexpr_to_expr.mli`:
- Line 63: The public quantifier APIs lack complete odoc parameter and return
documentation. In src/smtml/dolmenexpr_to_expr.mli lines 63-63, document var’s
string and ty parameters and returned term; in src/smtml/typed.mli lines
142-147, document vars and e plus the return values of forall and exists,
without changing their signatures or behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 80f1a65b-5543-466e-b6ef-93b8fe9daa62

📥 Commits

Reviewing files that changed from the base of the PR and between c753128 and 9d895fd.

📒 Files selected for processing (12)
  • src/smtml/bitwuzla_mappings.default.ml
  • src/smtml/cvc5_mappings.default.ml
  • src/smtml/dolmenexpr_to_expr.ml
  • src/smtml/dolmenexpr_to_expr.mli
  • src/smtml/mappings.ml
  • src/smtml/mappings.nop.ml
  • src/smtml/mappings_intf.ml
  • src/smtml/typed.ml
  • src/smtml/typed.mli
  • src/smtml/z3_mappings.default.ml
  • test/regression/dune
  • test/regression/test_issue_662.ml

Comment thread test/regression/test_issue_662.ml Outdated
Comment thread test/regression/test_issue_662.ml Outdated
@amenahh

amenahh commented Jul 30, 2026

Copy link
Copy Markdown
Contributor
let x = Smtml.Typed.Bitv32.symbol (Smtml.Symbol.make (Smtml.Ty.Ty_bitv 32) "x")

let exprx = Smtml.Typed.Unsafe.unwrap (Smtml.Typed.Bitv32.lt Smtml.Typed.Bitv32.one x)

let expr = Smtml.Expr.forall [ Smtml.Typed.Unsafe.unwrap x ] exprx


module CVC5 = Smtml.Solver.Batch (Smtml.Cvc5_mappings)

let solver = CVC5.create ()

let () =
  match CVC5.check solver [ expr ] with
  | `Sat -> Format.printf "SAT@\n"
  | `Unsat -> Format.printf "UNSAT@\n"
  | `Unknown -> Format.printf "UNKNOWN@\n"

module Bitwulza = Smtml.Solver.Batch (Smtml.Bitwuzla_mappings)

let solver = Bitwulza.create ()

let () =
  match Bitwulza.check solver [ expr ] with
  | `Sat -> Format.printf "SAT@\n"
  | `Unsat -> Format.printf "UNSAT@\n"
  | `Unknown -> Format.printf "UNKNOWN@\n"


module Z3 = Smtml.Solver.Batch (Smtml.Z3_mappings)

let solver = Z3.create ()

let () =
  match Z3.check solver [ expr ] with
  | `Sat -> Format.printf "SAT@\n"
  | `Unsat -> Format.printf "UNSAT@\n"
  | `Unknown -> Format.printf "UNKNOWN@\n"

for the following code CVC5 and Bitwulza return SAT and Z3 returns UNSAT

@filipeom

Copy link
Copy Markdown
Member Author

nice catch, I think this is happening because I'm only enconding the vars as variables in bitwuzla and cvc5 I would need to make x in the expression also a variable. Otherwise it's a symbol that isn't bound by any quantifier 😢

(* Ex. x != 0 is sat *)
let () =
let open Smtml in
let x = Typed.var Typed.Types.bitv32 "x" in

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Like this

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/smtml/dolmenexpr_to_expr.ml`:
- Around line 105-107: Update the var function in the Dolmen expression mapping
to construct a Dolmen Var term instead of delegating to const, ensuring forall
and exists receive variable descriptors. Add quantifier coverage through the
Dolmen-backed mapping for both universal and existential expressions.

In `@src/smtml/mappings.ml`:
- Around line 700-704: Restrict the earlier regex-literal matches for re.all,
re.none, and re.allchar to symbols whose namespace is Term and type is
Ty_regexp. Ensure Var symbols, including variables with these names, bypass
literal handling and reach make_var through the existing namespace dispatch.

In `@src/smtml/symbol.ml`:
- Line 90: Update Symbol.Smtlib.pp to render Var symbols using their names in
binder context instead of asserting, while preserving the existing handling for
other symbol kinds. Ensure Ast.Declare_const does not pass quantified variables
through this printer as top-level declarations, and remove assert false from any
reachable Var path, retaining assertions only where the type system proves
branches unreachable.

In `@src/smtml/symbol.mli`:
- Around line 60-74: Complete the odoc documentation for the public constructors
create, make_const, make, and make_var in the symbol interface by adding `@param`
tags for every argument and an `@return` tag describing the resulting symbol.
Preserve the existing descriptions and deprecation annotation, ensuring make
receives the same complete parameter and return documentation as the other
constructors.

In `@src/smtml/typed.mli`:
- Around line 43-54: Complete the odoc documentation for the public declarations
const, var, symbol, Bool.forall, and Bool.exists by adding `@param` tags for every
argument and an `@return` tag describing the result. Preserve the existing
descriptions and deprecation annotation while documenting each function’s
parameters and returned expression.

In `@test/regression/test_issue_662.ml`:
- Around line 37-42: Update the comment above the regression test to describe
the actual formula constructed by exprx and expr: forall x. 1 < x. Leave the
test implementation unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e450085-f960-4f77-846f-d03072cc83d8

📥 Commits

Reviewing files that changed from the base of the PR and between 9d895fd and 7f327ab.

📒 Files selected for processing (20)
  • docs/examples/product_mix.ml
  • src/smtml/altergo_mappings.default.ml
  • src/smtml/ast.ml
  • src/smtml/dolmenexpr_to_expr.ml
  • src/smtml/mappings.ml
  • src/smtml/model.ml
  • src/smtml/parser.mly
  • src/smtml/symbol.ml
  • src/smtml/symbol.mli
  • src/smtml/typed.ml
  • src/smtml/typed.mli
  • src/smtml/z3_mappings.default.ml
  • test/integration/test_solver.ml
  • test/regression/dune
  • test/regression/test_issue_655.ml
  • test/regression/test_issue_662.ml
  • test/test_harness.ml
  • test/unit/test_expr.ml
  • test/unit/test_model.ml
  • test/unit/test_smtlib_pp.ml
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/regression/dune

Comment thread src/smtml/dolmenexpr_to_expr.ml
Comment thread src/smtml/mappings.ml
Comment thread src/smtml/symbol.ml
Comment thread src/smtml/symbol.mli
Comment thread src/smtml/typed.mli
Comment thread test/regression/test_issue_662.ml Outdated
@filipeom

filipeom commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

I think I cracked it! You can now declared quantified variables using the API: https://github.com/formalsec/smtml/pull/669/changes#r3737779666

I was unaware, but we already had a way to support this with the current API. But since it was a bit awkward to use and unknown to most, I decided to take deeper look at things and try to improve it. In particular, I'm proposing we adopt the const API more commonly used in SMT solvers to replace the ambiguous symbol API which, with the introduction of Symbol.make_var and Typed.var, no longer makes sense. In summary, API changes I'm proposing are summarised below:

symbol.ml:
    
- create: Ty.t -> name -> namespace -> t        (was make3)
- make_const: Ty.t -> string -> t               (was make)
- make_var: Ty.t -> string -> t                 (new)
- make: deprecated alias for make_const
    
typed.ml:
    
- const: 'a ty -> string -> 'a expr             (was symbol)
- var: 'a ty -> string -> 'a expr               (new)
- symbol: deprecated alias for const

Adds quantifier support in cvc5 and bitwuzla. As well as, unifies the API
for creating symbols. In particular, we adopt the `const` API more
commonly used in SMT solvers to replace the ambiguous `symbol` API
which, with the introduction of `Symbol.make_var` and `Typed.var`,
no longer made sense. In summary, API changes are summarised below:

symbol.ml:

- create: Ty.t -> name -> namespace -> t        (was make3)
- make_const: Ty.t -> string -> t               (was make)
- make_var: Ty.t -> string -> t                 (new)
- make: deprecated alias for make_const

typed.ml:

- const: 'a ty -> string -> 'a expr             (was symbol)
- var: 'a ty -> string -> 'a expr               (new)
- symbol: deprecated alias for const

BREAKING CHANGE: Creation of symbols now uses const/var convention
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.

Unimplemented method for CVC5 and Bitwulza (exist)

2 participants