3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-12 01:56:22 +00:00

Merge remote-tracking branch 'origin/master' into c3

This commit is contained in:
CEisenhofer 2026-07-01 17:18:21 +02:00
commit 706f62286e
199 changed files with 11004 additions and 4584 deletions

View file

@ -35,7 +35,7 @@ Workflows may optionally include:
- Workflow files: `.github/workflows/*.md` and `.github/workflows/**/*.md`
- Workflow lock files: `.github/workflows/*.lock.yml`
- Shared components: `.github/workflows/shared/*.md`
- Configuration: `.github/aw/github-agentic-workflows.md`
- Configuration: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/github-agentic-workflows.md`
## Problems This Solves
@ -54,10 +54,12 @@ When you interact with this agent, it will:
## Available Prompts
> **Note**: The prompt and reference files listed below are located in the [`github/gh-aw`](https://github.com/github/gh-aw) repository and are **not available locally** in this repository. Load them from their public URLs.
### Create New Workflow
**Load when**: User wants to create a new workflow from scratch, add automation, or design a workflow that doesn't exist yet
**Prompt file**: `.github/aw/create-agentic-workflow.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/create-agentic-workflow.md`
**Use cases**:
- "Create a workflow that triages issues"
@ -67,7 +69,7 @@ When you interact with this agent, it will:
### Update Existing Workflow
**Load when**: User wants to modify, improve, or refactor an existing workflow
**Prompt file**: `.github/aw/update-agentic-workflow.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/update-agentic-workflow.md`
**Use cases**:
- "Add web-fetch tool to the issue-classifier workflow"
@ -77,7 +79,7 @@ When you interact with this agent, it will:
### Debug Workflow
**Load when**: User needs to investigate, audit, debug, or understand a workflow, troubleshoot issues, analyze logs, or fix errors
**Prompt file**: `.github/aw/debug-agentic-workflow.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/debug-agentic-workflow.md`
**Use cases**:
- "Why is this workflow failing?"
@ -87,7 +89,7 @@ When you interact with this agent, it will:
### Upgrade Agentic Workflows
**Load when**: User wants to upgrade workflows to a new gh-aw version or fix deprecations
**Prompt file**: `.github/aw/upgrade-agentic-workflows.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/upgrade-agentic-workflows.md`
**Use cases**:
- "Upgrade all workflows to the latest version"
@ -97,7 +99,7 @@ When you interact with this agent, it will:
### Create a Report-Generating Workflow
**Load when**: The workflow being created or updated produces reports — recurring status updates, audit summaries, analyses, or any structured output posted as a GitHub issue, discussion, or comment
**Prompt file**: `.github/aw/report.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/report.md`
**Use cases**:
- "Create a weekly CI health report"
@ -107,7 +109,7 @@ When you interact with this agent, it will:
### Create Shared Agentic Workflow
**Load when**: User wants to create a reusable workflow component or wrap an MCP server
**Prompt file**: `.github/aw/create-shared-agentic-workflow.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/create-shared-agentic-workflow.md`
**Use cases**:
- "Create a shared component for Notion integration"
@ -117,7 +119,7 @@ When you interact with this agent, it will:
### Fix Dependabot PRs
**Load when**: User needs to close or fix open Dependabot PRs that update dependencies in generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`)
**Prompt file**: `.github/aw/dependabot.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/dependabot.md`
**Use cases**:
- "Fix the open Dependabot PRs for npm dependencies"
@ -127,7 +129,7 @@ When you interact with this agent, it will:
### Analyze Test Coverage
**Load when**: The workflow reads, analyzes, or reports test coverage — whether triggered by a PR, a schedule, or a slash command. Always consult this prompt before designing the coverage data strategy.
**Prompt file**: `.github/aw/test-coverage.md`
**Prompt file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/test-coverage.md`
**Use cases**:
- "Create a workflow that comments coverage on PRs"
@ -137,7 +139,7 @@ When you interact with this agent, it will:
### CLI Commands Reference
**Load when**: The user asks how to run, compile, debug, or manage workflows from the command line; needs the MCP tool equivalent of a `gh aw` command; or is in a restricted environment (e.g., Copilot Cloud) without direct CLI access.
**Reference file**: `.github/aw/cli-commands.md`
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/cli-commands.md`
**Use cases**:
- "How do I trigger workflow X on the main branch?"
@ -148,7 +150,7 @@ When you interact with this agent, it will:
### Token Consumption Optimization
**Load when**: The user asks how to reduce token usage, lower workflow costs, make a workflow faster or cheaper, or measure the impact of prompt or configuration changes.
**Reference file**: `.github/aw/token-optimization.md`
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/token-optimization.md`
**Use cases**:
- "How do I reduce the token cost of this workflow?"
@ -161,7 +163,7 @@ When you interact with this agent, it will:
### Workflow Pattern Selection
**Load when**: The user asks for architecture, strategy, operating model selection, or pattern recommendations for building agentic workflows.
**Reference file**: `.github/aw/patterns.md`
**Reference file**: `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/patterns.md`
**Use cases**:
- "Which pattern should I use for multi-repo rollout?"
@ -174,7 +176,7 @@ When you interact with this agent, it will:
When a user interacts with you:
1. **Identify the task type** from the user's request
2. **Load the appropriate prompt** from the repository paths listed above
2. **Load the appropriate prompt** from the URLs listed above
3. **Follow the loaded prompt's instructions** exactly
4. **If uncertain**, ask clarifying questions to determine the right prompt
@ -213,12 +215,12 @@ gh aw compile --validate
## Important Notes
- Always reference the instructions file at `.github/aw/github-agentic-workflows.md` for complete documentation
- Always reference the instructions file at `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/github-agentic-workflows.md` for complete documentation
- Use the MCP tool `agentic-workflows` when running in GitHub Copilot Cloud
- Workflows must be compiled to `.lock.yml` files before running in GitHub Actions
- **Bash tools are enabled by default** - Don't restrict bash commands unnecessarily since workflows are sandboxed by the AWF
- Follow security best practices: minimal permissions, explicit network access, no template injection
- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See `.github/aw/network.md` for the full list of valid ecosystem identifiers and domain patterns.
- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/network.md` for the full list of valid ecosystem identifiers and domain patterns.
- **Single-file output**: When creating a workflow, produce exactly **one** workflow `.md` file. Do not create separate documentation files (architecture docs, runbooks, usage guides, etc.). If documentation is needed, add a brief `## Usage` section inside the workflow file itself.
- **Triggering runs**: Always use `gh aw run <workflow-name>` to trigger a workflow on demand — not `gh workflow run <file>.lock.yml`. `gh aw run` handles workflow resolution by short name, input parsing and validation, and correct run-tracking for agentic workflows. Use `--ref <branch>` to run on a specific branch.
- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see `.github/aw/cli-commands.md`
- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see `https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/cli-commands.md`

View file

@ -1,7 +1,8 @@
updates:
- directory: /
ignore:
- dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
- dependency-name: "github/gh-aw-actions/**"
- dependency-name: "github/gh-aw-actions" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
package-ecosystem: github-actions
schedule:
interval: weekly

9
.github/mcp.json vendored
View file

@ -1,10 +1,19 @@
{
"mcpServers": {
"github-agentic-workflows": {
"type": "local",
"command": "gh",
"args": [
"aw",
"mcp-server"
],
"tools": [
"compile",
"audit",
"logs",
"inspect",
"status",
"audit-diff"
]
}
}

View file

@ -0,0 +1,372 @@
---
name: agentic-workflow-designer
description: Conversational skill that interviews users to design new agentic workflows
disable-model-invocation: true
---
# Workflow Designer
Use this skill to run a structured interview with users who know their goal but not the workflow syntax yet, then generate one complete workflow `.md` file.
## When to Use This Skill
Use this before `.github/aw/create-agentic-workflow.md` when requirements are unclear or incomplete.
- Use `skills/agentic-workflow-designer/SKILL.md` to discover and confirm requirements.
- Use `.github/aw/create-agentic-workflow.md` once requirements are clear and ready for implementation.
- Use `.github/aw/agentic-chat.md` when the user wants a specification/pseudo-code instead of a runnable workflow file.
## Interview Framework
Ask one question at a time. Move to the next phase only after the current phase is clear.
### Phase 1: Goal
Ask: **"What do you want to automate?"**
Capture:
- Workflow name (kebab-case candidate)
- Brief description
- Optional emoji
### Phase 2: Trigger
Ask: **"When should this run?"**
Follow up only if needed:
- Which event type(s)?
- Any filters (labels, branches, commands)?
- Scheduled cadence (daily/weekly/hourly)?
Map to the `on:` block.
### Phase 3: Scope (Read/Write)
Ask:
- **"What should it read?"** (issues, PRs, code, discussions, CI data)
- **"What should it create or update?"** (comments, issues, PRs, labels)
Map to:
- `permissions:` (keep read-only for agent job)
- `tools:`
- `safe-outputs:`
### Phase 4: Data Strategy
Ask:
- **"What data does the agent need to make decisions?"**
- Follow up: **"Can we pre-fetch and aggregate that data with shell commands so the agent only reads compact JSON?"**
Capture:
- Whether `steps:` should pre-fetch GitHub data with `gh` + `jq`
- Output paths under `/tmp/gh-aw/data/`
- Whether batch work should use sub-agents
Map to:
- `steps:`
- Prompt references to pre-computed file paths
### Phase 5: Guardrails
Ask: **"Should it block merging, just advise, or silently log?"**
Capture:
- Visibility expectations (comment, issue, no visible output)
- No-op behavior expectation
Guide toward safe output behavior and explicit `noop` instructions.
### Phase 6: Context & Network
Ask: **"Does it need external APIs, web access, package installs, or MCP servers?"**
Follow up:
- **"Any third-party services or MCP servers to include (for example Slack, Jira, Datadog, custom internal MCP)?"**
- **"Are you deploying on GitHub.com, GHEC with custom endpoints, or GHES?"**
- For each integration, identify required auth from source docs and map it to GitHub Actions secrets + workflow env variables.
- Ask for exact external domains (FQDN/wildcard).
Map to:
- `network.allowed`
- Optional MCP/GitHub tool usage in `tools:`
- `secrets:` / `env:` wiring for integration tokens
- GHES/GHEC settings such as `engine.api-target` and `aw.json` `ghes: true` (when applicable)
### Phase 7: Engine (optional)
Ask only if ambiguous: **"Any AI engine preference?"**
If no preference, suggest default:
- "I'd suggest Copilot since you haven't mentioned a preference. Sound good?"
Map to `engine:` only when not default.
### Phase 8: Confirmation
Present a structured summary and ask for approval before generation.
## Decision Heuristics
### Trigger Mapping
| User says... | Maps to |
|---|---|
| "when someone opens a PR" | `on: pull_request:` with `types: [opened]` |
| "when a PR is updated" | `on: pull_request:` with `types: [opened, synchronize]` |
| "every morning", "daily" | fuzzy schedule shorthand `on: schedule: daily on weekdays` (compiler expands to cron) |
| "every Monday", "weekly" | fuzzy schedule shorthand `on: schedule: weekly` (compiler expands to cron) |
| "when I say /review" | `on: slash_command:` with `name: review` (or requested command) |
| "when an issue is labeled bug" | `on: issues:` with `types: [labeled]` and label filter guidance |
| "run when label ai-review is added" | `on: label_command:` with `name`/`names`, optional event scoping, and label-as-command semantics |
| "run on PRs from forks" | `on: pull_request:` plus explicit `forks:` allowlist and fork security guardrails |
| "sometimes automatic, sometimes manual" | semi-active pattern: combine `schedule`/event triggers with `workflow_dispatch` |
| "manually", "on demand" | `on: workflow_dispatch:` |
| "when a deployment fails" | `on: deployment_status:` |
| "when another workflow finishes" | `on: workflow_run:` |
### Safe Output Mapping
| User says... | Maps to |
|---|---|
| "post a comment" | `add-comment` |
| "create an issue" | `create-issue` |
| "update issue title/body" | `update-issue` |
| "close the issue" | `close-issue` |
| "assign someone", "remove assignment" | `assign-to-user`, `unassign-from-user` |
| "set issue type/field/milestone" | `set-issue-type`, `set-issue-field`, `assign-milestone` |
| "open a PR", "submit changes" | `create-pull-request` |
| "update PR description/title" | `update-pull-request` |
| "close the PR", "merge the PR" | `close-pull-request`, `merge-pull-request` |
| "mark PR ready", "sync PR branch" | `mark-pull-request-as-ready-for-review`, `update-branch` |
| "commit a fix to the PR branch" | `push-to-pull-request-branch` |
| "approve / request changes" | `submit-pull-request-review` |
| "inline review comment", "reply to review thread" | `create-pull-request-review-comment`, `reply-to-pull-request-review-comment`, `resolve-pull-request-review-thread` |
| "start or edit discussion", "close discussion" | `create-discussion`, `update-discussion`, `close-discussion` |
| "request reviewer", "hide comment" | `add-reviewer`, `hide-comment` |
| "create/update project", "project status update" | `create-project`, `update-project`, `create-project-status-update` |
| "update release", "upload release asset" | `update-release`, `upload-asset` |
| "create/auto-fix code scan alert" | `create-code-scanning-alert`, `autofix-code-scanning-alert` |
| "start an agent session", "assign to an agent" | `create-agent-session`, `assign-to-agent` |
| "store persistent memory comment" | `comment-memory` |
| "link a sub-issue" | `link-sub-issue` |
| "add labels", "remove labels" | `add-labels`, `remove-labels` |
| "nothing visible", "just analyze" | no safe outputs required |
### Network Mapping
| User says... | Maps to |
|---|---|
| "calls an external API" | ask for exact FQDN/wildcard, then add to `network.allowed` |
| "reads GitHub data / clones repos" | include `github` in `network.allowed` |
| "uses GitHub Actions artifacts or cache" | include `github-actions` in `network.allowed` |
| "installs npm packages" | include `node` in `network.allowed` |
| "runs pip install" | include `python` in `network.allowed` |
| "builds Go code" | include `go` in `network.allowed` |
| "installs gems / uses Bundler" | include `ruby` in `network.allowed` |
| "runs cargo build" | include `rust` in `network.allowed` |
| "uses NuGet / .NET restore" | include `dotnet` in `network.allowed` |
| "builds with Maven / Gradle" | include `java` in `network.allowed` |
| "uses Docker / pulls container images / pushes to GHCR" | include `containers` in `network.allowed` |
| "runs Playwright browser tests" | include `playwright` in `network.allowed` |
| "runs apt install / yum / apk" | include `linux-distros` in `network.allowed` |
| "uses Terraform / HashiCorp registry" | include `terraform` in `network.allowed` |
| "connects to localhost / loopback / local services" | include `local` in `network.allowed` |
| "uses Swift Package Manager" | include `swift` in `network.allowed` |
| "uses Composer / PHP packages" | include `php` in `network.allowed` |
| "uses pub.dev / Dart packages" | include `dart` in `network.allowed` |
| "uses Hackage / Haskell packages" | include `haskell` in `network.allowed` |
| "uses CPAN / Perl packages" | include `perl` in `network.allowed` |
| "serves or loads web fonts" | include `fonts` in `network.allowed` |
| "uses Deno or JSR packages" | include `deno` in `network.allowed` |
| "uses Elixir / Hex packages" | include `elixir` in `network.allowed` |
| "uses Bazel build" | include `bazel` in `network.allowed` |
| "uses R / CRAN packages" | include `r` in `network.allowed` |
| "no external access" | `network.allowed: [defaults]` (or `[]` if explicitly zero network) |
### Tool Mapping
| User says... | Maps to |
|---|---|
| "read GitHub issues/PRs/workflows" | `tools.github` with `mode: gh-proxy` and minimal `toolsets` |
| "use full MCP server/tool definitions" | `tools.github` with `mode: local` |
| "use other MCP servers but keep token cost down" | `tools.cli-proxy: true` (hybrid CLI-proxy mode) |
| "edit files" | `edit` tool (default unless restricted) |
| "run commands/tests" | `bash` tool (default unless restricted) |
| "browse web pages/docs" | `web-fetch` and/or `web-search` |
| "test UI flows" | `playwright` |
### Pattern Heuristics
| User says... | Recommended named pattern |
|---|---|
| "triage issues automatically" | `IssueOps` |
| "run on /commands with human approval loops" | `ChatOps` |
| "run every weekday and keep improving" | `DailyOps` |
| "monitor workflow failures and trends" | `MonitorOps` |
| "process a big backlog in chunks" | `BatchOps` |
| "run manually with input parameters" | `DispatchOps` |
| "apply a label-based workflow" | `LabelOps` |
| "operate across multiple repositories" | `MultiRepoOps` |
| "coordinate multiple sub-agents" | `Orchestration` |
| "manage project board items" | `ProjectOps` |
| "research, plan, and assign issues" | `ResearchPlanAssignOps` |
| "self-correcting / retry on failure" | `CorrectionOps` |
| "run in a side/fork repo" | `SideRepoOps` |
| "write a spec before implementing" | `SpecOps` |
| "A/B test workflow variants" | `TrialOps` |
| "process items from a queue" | `WorkQueueOps` |
| "deterministic, no LLM needed" | `DeterministicOps` |
| "manage from a central repo" | `CentralRepoOps` |
| "track work via GitHub Projects" | `Monitoring with Projects` |
### Integration Auth Mapping
When the user names a third-party service or MCP server:
1. Confirm whether native tool, MCP server, or safe-output job is the right integration path.
2. Look up the integration's auth requirements and required scopes before finalizing the design.
3. Provide a concrete setup checklist with:
- required GitHub Actions secrets (names to create)
- workflow env variables that consume those secrets
- minimum token scopes/permissions needed
Output format to use:
```text
Integration auth setup:
- <service-or-mcp>: <purpose>
- Secrets to create: <SECRET_NAME>, <SECRET_NAME>
- Workflow env vars: <ENV_VAR>=${{ secrets.<SECRET_NAME> }}
- Required scopes/permissions: <least-privilege scopes>
```
Never suggest committing plaintext tokens.
### Data Strategy Mapping
| User says... | Maps to |
|---|---|
| "analyze PRs", "review issues", "check status" | add `steps:` that pre-fetch with `gh` + `jq` |
| "read the diff", "look at changed files" | add `steps:` using `gh pr diff` or `gh pr view --json files` |
| "search for patterns across repos" | add `steps:` using `gh search` + `jq` filters |
| "just respond to a comment" | no pre-fetch needed (event payload is enough) |
| "process each item individually" | suggest sub-agent pattern with `model: small` |
## Token Optimization Defaults
Apply these defaults unless the user explicitly asks otherwise:
1. Use DataOps by default for GitHub reads: pre-fetch/aggregate with `gh` + `jq` in `steps:`, store compact JSON in `/tmp/gh-aw/data/`, and point the prompt to those files (see `.github/aw/token-optimization.md` for details).
2. Keep tool surface minimal: default to `tools.github.mode: gh-proxy`, include only required toolsets, and prefer `bash` + `gh` for simple reads.
3. For batch workloads, split items into compact data and suggest sub-agent processing with `model: small`.
4. Keep prompts compact: concise imperative instructions, explicit file paths, single-line `noop` guidance, and stable instructions before dynamic content.
## Progressive Disclosure Rules
1. Never dump all options at once; ask one targeted question at a time.
2. Skip questions when answers are inferable from prior user statements.
3. Offer smart defaults and request confirmation instead of over-questioning.
4. Ask at most 5 questions before presenting a summary; then ask "anything else?" if needed.
5. Detect done signals (`that's it`, `looks good`, `generate it`) and proceed to generation.
## Confirmation Format
Use this exact structure:
```text
📋 Proposed workflow:
- Name: <workflow-id>
- Trigger: <event + key options>
- Engine: <engine or default>
- Tools: <tool summary>
- Safe outputs: <list or none>
- Network: <allowed summary>
- Integrations/Auth: <service/mcp + required secrets/env vars>
- Deployment: <GitHub.com or GHEC/GHES details>
- Intent: <one-sentence task>
```
Then ask: **"Ready to generate, or want to adjust anything?"**
## Generation Template
After confirmation, generate one workflow file using the same skeleton style as `.github/aw/create-agentic-workflow.md`.
```markdown
---
emoji: <emoji>
description: <brief description>
on:
<trigger config>
permissions:
contents: read
issues: read
pull-requests: read
tools:
github:
mode: gh-proxy
toolsets: [default]
steps:
- name: <optional data prefetch>
run: |
mkdir -p /tmp/gh-aw/data
<gh + jq commands that produce compact JSON>
safe-outputs:
<safe-output-types-if-needed>
network:
allowed:
- defaults
- <additional entries if needed>
---
# <Workflow Name>
## Task
<clear instructions tied to trigger context>
If `steps:` includes pre-fetch commands, read the resulting `/tmp/gh-aw/data/*.json` files instead of broad live re-fetches.
## Safe Outputs
- Use configured safe outputs for all visible write actions.
- Call `noop` with a short reason when no action is needed.
```
## Validation Checklist
Before final output, run this internal self-check:
- [ ] Agent job permissions remain read-only (writes only via safe outputs)
- [ ] `safe-outputs:` covers every write action mentioned in prompt/instructions
- [ ] Network access is scoped; avoid blanket wildcard entries
- [ ] Trigger matches the user's intended activation event
- [ ] Prompt instructs agent to call `noop` when no action is needed
- [ ] Unnecessary defaults are omitted (for example `engine: copilot`)
- [ ] If reading GitHub data, `steps:` pre-fetches compact JSON (DataOps)
- [ ] `tools.github.mode` is `gh-proxy` unless broader MCP toolsets are explicitly needed
- [ ] Only required toolsets are listed (avoid blanket toolset lists)
- [ ] Prompt references specific pre-computed file paths
- [ ] For batch processing (>5 items), sub-agent pattern is suggested
- [ ] For each third-party service/MCP integration, required secrets/env vars are listed
- [ ] Auth guidance includes least-privilege token scope recommendations
- [ ] For GHEC/GHES deployments, `engine.api-target` and GHES compatibility guidance are included when needed
## References (load only when needed)
In-repo references:
- `.github/aw/syntax.md` (index → `.github/aw/syntax-core.md`, `.github/aw/syntax-agentic.md`, `.github/aw/syntax-tools-imports.md`)
- `.github/aw/safe-outputs.md` (index → `.github/aw/safe-outputs-content.md`, `.github/aw/safe-outputs-management.md`, `.github/aw/safe-outputs-automation.md`, `.github/aw/safe-outputs-runtime.md`)
- `.github/aw/network.md`
- `.github/aw/patterns.md`
- `.github/aw/subagents.md`
- `.github/aw/token-optimization.md`
- `.github/aw/triggers.md`
- `.github/aw/create-agentic-workflow.md`
Portable HTTPS references:
- `https://github.com/github/gh-aw/blob/main/.github/aw/syntax.md` (index → `.../syntax-core.md`, `.../syntax-agentic.md`, `.../syntax-tools-imports.md`)
- `https://github.com/github/gh-aw/blob/main/.github/aw/safe-outputs.md` (index → `.../safe-outputs-content.md`, `.../safe-outputs-management.md`, `.../safe-outputs-automation.md`, `.../safe-outputs-runtime.md`)
- `https://github.com/github/gh-aw/blob/main/.github/aw/network.md`
- `https://github.com/github/gh-aw/blob/main/.github/aw/patterns.md`
- `https://github.com/github/gh-aw/blob/main/.github/aw/triggers.md`
- `https://github.com/github/gh-aw/blob/main/.github/aw/create-agentic-workflow.md`

View file

@ -32,6 +32,7 @@ Load these files from `github/gh-aw` (they are not available locally).
- `.github/aw/memory.md`
- `.github/aw/messages.md`
- `.github/aw/network.md`
- `.github/aw/optimize-agentic-workflow.md`
- `.github/aw/patterns.md`
- `.github/aw/pr-reviewer.md`
- `.github/aw/report.md`

View file

@ -28,7 +28,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- run: |
@ -37,9 +37,10 @@ jobs:
${{ matrix.cmd1 }}
${{ matrix.cmd2 }}
${{ matrix.cmd3 }}
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }}
cmake ${{ matrix.bindings }} -G "NMake Makefiles" ../
nmake
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }} || exit /b 1
cmake ${{ matrix.bindings }} -G "NMake Makefiles" ../ || exit /b 1
nmake || exit /b 1
cd ..
shell: cmd
- name: Run Regressions
@ -52,7 +53,8 @@ jobs:
if: ${{ matrix.test }}
run: |
pushd build
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }}
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }} || exit /b 1
pushd build\python
python z3test.py z3
python z3test.py z3num

20
.github/workflows/a3-python.lock.yml generated vendored
View file

@ -31,13 +31,13 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -91,7 +91,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -152,7 +152,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -377,7 +377,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -398,7 +398,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@ -999,7 +999,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1194,7 +1194,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1222,7 +1222,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1454,7 +1454,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,5 @@
# This file was automatically generated by pkg/workflow/maintenance_workflow.go (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
#
# ___ _ _
# / _ \ | | (_)
# | |_| | __ _ ___ _ __ | |_ _ ___
@ -12,7 +14,6 @@
# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
#
# This file was automatically generated by pkg/workflow/maintenance_workflow.go (v0.79.6). DO NOT EDIT.
#
# To regenerate this workflow, run:
# gh aw compile
@ -20,15 +21,17 @@
#
# For more information: https://github.github.com/gh-aw/introduction/overview/
#
# Alternative regeneration methods:
# make recompile
# This file defines the generated agentic maintenance workflow for this repository.
# It runs scheduled cleanup for expiring safe outputs and supports manual maintenance operations.
#
# Or use the gh-aw CLI directly:
# ./gh-aw compile --validate --verbose
# This workflow is generated automatically when workflows use expiring safe outputs
# or when repository maintenance features are enabled in .github/workflows/aw.json.
#
# The workflow is generated when any workflow uses the 'expires' field
# in create-discussions, create-issues, or create-pull-request safe-outputs configuration.
# Schedule frequency is automatically determined by the shortest expiration time.
# To disable maintenance workflow generation, set in .github/workflows/aw.json:
# {"maintenance": false}
#
# Agentic maintenance docs:
# https://github.github.com/gh-aw/reference/ephemerals/#manual-maintenance-operations
#
name: Agentic Maintenance
@ -93,7 +96,7 @@ jobs:
pull-requests: write
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -131,7 +134,7 @@ jobs:
actions: write
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -155,12 +158,12 @@ jobs:
operation: ${{ steps.record.outputs.operation }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -175,9 +178,9 @@ jobs:
await main();
- name: Install gh-aw
uses: github/gh-aw-actions/setup-cli@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup-cli@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
version: v0.79.6
version: v0.80.9
- name: Run operation
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@ -195,7 +198,9 @@ jobs:
- name: Record outputs
id: record
run: echo "operation=${{ inputs.operation }}" >> "$GITHUB_OUTPUT"
env:
GH_AW_OPERATION: ${{ inputs.operation }}
run: echo "operation=$GH_AW_OPERATION" >> "$GITHUB_OUTPUT"
update_pull_request_branches:
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'update_pull_request_branches' && (!(github.event.repository.fork)) }}
@ -205,7 +210,7 @@ jobs:
pull-requests: write
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -244,14 +249,14 @@ jobs:
run_url: ${{ steps.record.outputs.run_url }}
steps:
- name: Checkout actions folder
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
sparse-checkout: |
actions
persist-credentials: false
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -280,7 +285,9 @@ jobs:
- name: Record outputs
id: record
run: echo "run_url=${{ inputs.run_url }}" >> "$GITHUB_OUTPUT"
env:
GH_AW_RUN_URL: ${{ inputs.run_url }}
run: echo "run_url=$GH_AW_RUN_URL" >> "$GITHUB_OUTPUT"
create_labels:
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'create_labels' && (!(github.event.repository.fork)) }}
@ -290,12 +297,12 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -310,9 +317,9 @@ jobs:
await main();
- name: Install gh-aw
uses: github/gh-aw-actions/setup-cli@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup-cli@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
version: v0.79.6
version: v0.80.9
- name: Create missing labels
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@ -336,12 +343,12 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -356,13 +363,13 @@ jobs:
await main();
- name: Install gh-aw
uses: github/gh-aw-actions/setup-cli@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup-cli@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
version: v0.79.6
version: v0.80.9
- name: Restore activity report logs cache
id: activity_report_logs_cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: ./.cache/gh-aw/activity-report-logs
key: ${{ runner.os }}-activity-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}
@ -379,10 +386,10 @@ jobs:
${GH_AW_CMD_PREFIX} logs \
--repo "${{ github.repository }}" \
--start-date -1w \
--count 100 \
--count 500 \
--output ./.cache/gh-aw/activity-report-logs \
--format markdown \
> ./.cache/gh-aw/activity-report-logs/report.md
--report-file ./.cache/gh-aw/activity-report-logs/report.md
- name: Save activity report logs cache
if: ${{ always() }}
@ -441,12 +448,12 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -461,13 +468,13 @@ jobs:
await main();
- name: Install gh-aw
uses: github/gh-aw-actions/setup-cli@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup-cli@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
version: v0.79.6
version: v0.80.9
- name: Restore forecast report logs cache
id: forecast_report_logs_cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
path: ./.github/aw/logs
key: ${{ runner.os }}-forecast-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}
@ -538,7 +545,7 @@ jobs:
issues: write
steps:
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -570,12 +577,12 @@ jobs:
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
destination: ${{ runner.temp }}/gh-aw/actions
@ -590,9 +597,9 @@ jobs:
await main();
- name: Install gh-aw
uses: github/gh-aw-actions/setup-cli@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6
uses: github/gh-aw-actions/setup-cli@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with:
version: v0.79.6
version: v0.80.9
- name: Validate workflows and file issue on findings
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0

View file

@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Configure CMake and build
run: |

File diff suppressed because one or more lines are too long

View file

@ -32,13 +32,13 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -92,7 +92,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -153,7 +153,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -376,7 +376,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -397,7 +397,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@ -1003,7 +1003,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1196,7 +1196,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1224,7 +1224,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1456,7 +1456,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1500,7 +1500,7 @@ jobs:
await main();
- name: Checkout repository (trusted default branch for comment events)
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.repository.default_branch }}
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@ -1508,7 +1508,7 @@ jobs:
fetch-depth: 1
- name: Checkout repository
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}

View file

@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -45,7 +45,7 @@ jobs:
- name: Restore or create cache
id: cache-z3
uses: actions/cache@v5.0.5
uses: actions/cache@v6.0.0
with:
path: |
build/z3

View file

@ -38,7 +38,7 @@ jobs:
runRegressions: false
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -81,7 +81,7 @@ jobs:
container: "quay.io/pypa/manylinux_2_34_x86_64:latest"
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Select Python
run: |
@ -121,7 +121,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download ARM toolchain
run: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz'
@ -165,7 +165,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup OCaml
uses: ocaml/setup-ocaml@v3
@ -220,7 +220,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup OCaml
uses: ocaml/setup-ocaml@v3
@ -248,7 +248,19 @@ jobs:
cd ..
- name: Install Z3 OCaml package
run: eval `opam config env`; ocamlfind install z3-static build/api/ml/* build/libz3-static.a
run: |
eval `opam config env`
ocamlfind install z3-static build/api/ml/* build/libz3-static.a
# Ensure the stublibs directory where dllz3ml-static.so was installed is
# listed in ld.conf so the OCaml bytecode linker can find it. When no
# explicit -dll flag is passed, ocamlfind installs dll* files to stublibs
# but may not update ld.conf, causing "Fatal error: exception End_of_file"
# in ocamlc when it cannot locate the stub shared library at link time.
STUBLIBS="$(ocamlfind printconf destdir)/stublibs"
LDCONF="$(ocamlfind printconf ldconf)"
if [ -d "$STUBLIBS" ] && ! grep -qF "$STUBLIBS" "$LDCONF" 2>/dev/null; then
echo "$STUBLIBS" >> "$LDCONF"
fi
- name: Build and run OCaml examples
run: |
@ -314,7 +326,7 @@ jobs:
runTests: false
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -404,7 +416,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -453,7 +465,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -494,7 +506,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -514,7 +526,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6

View file

@ -31,15 +31,15 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -93,7 +93,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -154,7 +154,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -381,7 +381,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -402,7 +402,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@ -415,7 +415,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1087,7 +1087,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1281,7 +1281,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1309,7 +1309,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1541,7 +1541,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1617,7 +1617,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -34,13 +34,13 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -97,7 +97,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -163,7 +163,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -393,7 +393,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -415,7 +415,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@ -1021,7 +1021,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1224,7 +1224,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1253,7 +1253,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1457,7 +1457,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1535,7 +1535,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1580,7 +1580,7 @@ jobs:
await main();
- name: Checkout repository (trusted default branch for comment events)
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && (github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.repository.default_branch }}
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@ -1588,7 +1588,7 @@ jobs:
fetch-depth: 1
- name: Checkout repository
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') && github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ steps.extract-base-branch.outputs.base-branch || github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}

View file

@ -31,13 +31,13 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -90,7 +90,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -151,7 +151,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -377,7 +377,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -398,7 +398,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@ -990,7 +990,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1183,7 +1183,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1211,7 +1211,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1441,7 +1441,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -19,7 +19,7 @@ jobs:
COV_DETAILS_PATH: ${{github.workspace}}/cov-details
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.0
- name: Setup
run: |

View file

@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Install cross build tools
run: apt update && apt install -y ninja-build cmake python3 g++-13-${{ matrix.arch }}-linux-gnu

View file

@ -31,16 +31,16 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -94,7 +94,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -155,7 +155,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -387,7 +387,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -414,7 +414,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@ -422,7 +422,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1035,7 +1035,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1229,7 +1229,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1257,7 +1257,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1487,7 +1487,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1563,7 +1563,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Go
uses: actions/setup-go@v6
@ -46,7 +46,7 @@ jobs:
needs: build-go-docs
steps:
- name: Checkout
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup node
uses: actions/setup-node@v6

View file

@ -56,7 +56,7 @@ jobs:
DISCUSSION_CATEGORY: ${{ github.event.inputs.discussion_category || 'Agentic Workflows' }}
steps:
- name: Checkout Z3
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
with:
ref: ${{ env.Z3_REF }}
fetch-depth: 1

File diff suppressed because one or more lines are too long

View file

@ -34,16 +34,16 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -110,7 +110,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -173,7 +173,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -415,7 +415,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -442,7 +442,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@ -450,7 +450,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1076,7 +1076,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1268,7 +1268,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1296,7 +1296,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1500,7 +1500,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1561,7 +1561,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1637,7 +1637,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -31,7 +31,7 @@ jobs:
ASAN_OPTIONS: "detect_leaks=1:halt_on_error=0:print_stats=1:log_path=/tmp/asan"
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -121,7 +121,7 @@ jobs:
UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=0:log_path=/tmp/ubsan"
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6

View file

@ -14,7 +14,7 @@ jobs:
BUILD_TYPE: Release
steps:
- name: Checkout Repo
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Build
run: |

View file

@ -14,7 +14,7 @@ jobs:
BUILD_TYPE: Release
steps:
- name: Checkout Repo
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Build
run: |

View file

@ -27,7 +27,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup .NET
uses: actions/setup-dotnet@v5
@ -87,7 +87,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup .NET
uses: actions/setup-dotnet@v5
@ -142,7 +142,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup .NET
uses: actions/setup-dotnet@v5
@ -197,7 +197,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup .NET
uses: actions/setup-dotnet@v5
@ -256,7 +256,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download Windows x64 build from release
env:
@ -292,7 +292,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download Windows x86 build from release
env:
@ -328,7 +328,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download Ubuntu x64 build from release
env:
@ -361,7 +361,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS x64 build from release
env:
@ -394,7 +394,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 build from release
env:
@ -431,7 +431,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -470,7 +470,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -510,7 +510,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -553,7 +553,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -582,7 +582,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -611,7 +611,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -640,7 +640,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -672,7 +672,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -727,7 +727,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS x64 build from release
env:
@ -779,7 +779,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 build from release
env:
@ -835,7 +835,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -844,3 +844,97 @@ jobs:
- name: Run build script unit tests
run: python -m unittest discover -s scripts/tests -p "test_*.py" -v
# ============================================================================
# DOTNET MANAGED WRAPPER ARCHITECTURE VALIDATION
# ============================================================================
validate-dotnet-anycpu:
name: "Validate Microsoft.Z3.dll is AnyCPU (issue #9863)"
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v7.0.0
- name: Download NuGet package from release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ github.event.inputs.release_tag }}"
if [ -z "$TAG" ]; then
TAG="Nightly"
fi
gh release download $TAG --pattern "*.nupkg" --dir nuget-packages
- name: Extract managed DLL from NuGet package
run: |
# NuGet packages are ZIP archives; exclude the symbols package
NUPKG=$(ls nuget-packages/*.nupkg | grep -v '\.symbols\.' | grep -v '\.snupkg' | head -n 1)
echo "Checking package: $NUPKG"
unzip -q "$NUPKG" "lib/netstandard2.0/Microsoft.Z3.dll" -d nupkg-extracted
- name: Check PE Machine field is AnyCPU (not architecture-specific)
run: |
python3 - <<'EOF'
import struct
import sys
dll_path = "nupkg-extracted/lib/netstandard2.0/Microsoft.Z3.dll"
with open(dll_path, 'rb') as f:
# Verify MZ magic
if f.read(2) != b'MZ':
print("ERROR: Not a valid PE file (missing MZ header)")
sys.exit(1)
# Read PE header offset stored at 0x3C in the DOS stub
f.seek(0x3C)
pe_offset = struct.unpack('<I', f.read(4))[0]
# Verify PE signature
f.seek(pe_offset)
if f.read(4) != b'PE\x00\x00':
print("ERROR: Missing PE\\0\\0 signature")
sys.exit(1)
# COFF Machine field is the 2 bytes immediately after the PE signature
machine = struct.unpack('<H', f.read(2))[0]
machine_names = {
0x0000: "Unknown/AnyCPU",
0x014C: "i386 (AnyCPU for managed assemblies)",
0x8664: "AMD64/x64",
0xAA64: "ARM64",
0x01C0: "ARM",
0x01C4: "ARM Thumb-2",
}
machine_name = machine_names.get(machine, f"0x{machine:04X}")
print(f"Machine field: 0x{machine:04X} = {machine_name}")
if machine == 0x8664:
print()
print("FAIL: Machine is AMD64 (0x8664).")
print("This prevents loading on arm64 .NET hosts even though the assembly")
print("contains only pure IL (CorFlags.ILONLY=True).")
print("The DLL must be built as AnyCPU (Machine=0x014C) so the CLR loader")
print("accepts it on every host architecture.")
print("See issue #9863 for details.")
sys.exit(1)
elif machine == 0xAA64:
print()
print("FAIL: Machine is ARM64 (0xAA64).")
print("This prevents loading on x64 .NET hosts.")
print("The DLL must be built as AnyCPU (Machine=0x014C).")
sys.exit(1)
elif machine in (0x014C, 0x0000):
print()
print("PASS: Machine field indicates AnyCPU.")
print("Microsoft.Z3.dll will load on any .NET host architecture.")
else:
print()
print(f"FAIL: Unexpected Machine field 0x{machine:04X}.")
print("Expected 0x014C (i386/AnyCPU) for a managed-only assembly.")
sys.exit(1)
EOF

View file

@ -35,7 +35,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -71,7 +71,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -112,7 +112,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS x64 Build
uses: actions/download-artifact@v8.0.1
@ -171,7 +171,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 Build
uses: actions/download-artifact@v8.0.1
@ -229,7 +229,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -258,7 +258,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -293,7 +293,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -349,7 +349,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Select Python
run: |
@ -387,7 +387,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download ARM toolchain
run: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz'
@ -435,7 +435,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download RISC-V toolchain
run: curl -L -o /tmp/riscv-toolchain.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.09.03/riscv64-glibc-ubuntu-20.04-gcc-nightly-2024.09.03-nightly.tar.gz'
@ -483,66 +483,13 @@ jobs:
path: src/api/python/wheelhouse/*.whl
retention-days: 2
pyodide-python:
name: "Python bindings (Pyodide)"
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
- name: Setup packages
run: sudo apt-get update && sudo apt-get install -y python3-dev python3-pip python3-venv
- name: Create venv
run: python3 -m venv ~/env
- name: Install pyodide
run: ~/env/bin/pip install pyodide-build pyodide-cli
- name: Configure Emscripten
run: |
git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
cd ~/emsdk
PYODIDE_EMSCRIPTEN_VERSION=$(~/env/bin/pyodide config get emscripten_version)
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
- name: Build wheel
run: |
source ~/emsdk/emsdk_env.sh
cd src/api/python
CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" CXXFLAGS="${CXXFLAGS}" ~/env/bin/pyodide build --exports whole_archive
env:
CFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0 -g2"
LDFLAGS: "-fexceptions -s WASM_BIGINT"
CXXFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0"
- name: Setup env-pyodide
run: |
source ~/env/bin/activate
source ~/emsdk/emsdk_env.sh
pyodide venv ~/env-pyodide
- name: Test wheel
run: |
~/env-pyodide/bin/pip install src/api/python/dist/*.whl
~/env-pyodide/bin/python src/api/python/z3test.py z3
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: PyodidePythonBuild
path: src/api/python/dist/*.whl
retention-days: 2
windows-build-x64:
name: "Windows x64 build"
runs-on: windows-latest
timeout-minutes: 120
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -552,7 +499,8 @@ jobs:
- name: Build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64 || exit /b 1
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
@ -568,7 +516,7 @@ jobs:
timeout-minutes: 120
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -578,7 +526,8 @@ jobs:
- name: Build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x86 || exit /b 1
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
@ -594,7 +543,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -604,7 +553,8 @@ jobs:
- name: Build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 || exit /b 1
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }} --zip
- name: Upload artifact
@ -624,7 +574,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -699,7 +649,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -740,11 +690,11 @@ jobs:
python-package:
name: "Python packaging"
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64, pyodide-python]
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64]
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -799,12 +749,6 @@ jobs:
name: ManyLinuxPythonBuildRiscv64
path: artifacts
- name: Download Pyodide Build
uses: actions/download-artifact@v8.0.1
with:
name: PyodidePythonBuild
path: artifacts
- name: Extract builds
run: |
cd artifacts
@ -865,7 +809,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download all artifacts
uses: actions/download-artifact@v8.0.1

View file

@ -20,7 +20,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -30,7 +30,8 @@ jobs:
- name: Build Windows x64
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64 || exit /b 1
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.0' }} --zip
- name: Upload Windows x64 artifact
@ -44,7 +45,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -54,7 +55,8 @@ jobs:
- name: Build Windows x86
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x86 || exit /b 1
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.0' }} --zip
- name: Upload Windows x86 artifact
@ -68,7 +70,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -78,7 +80,8 @@ jobs:
- name: Build Windows ARM64
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 || exit /b 1
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.0' }} --zip
- name: Upload Windows ARM64 artifact
@ -92,7 +95,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -113,7 +116,7 @@ jobs:
runs-on: macos-14
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -134,7 +137,7 @@ jobs:
runs-on: macos-14
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -157,7 +160,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -212,7 +215,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6

View file

@ -17,11 +17,11 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
# Cache ccache (shared across runs)
- name: Cache ccache
uses: actions/cache@v5.0.5
uses: actions/cache@v6.0.0
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@ -30,7 +30,7 @@ jobs:
# Cache opam (compiler + packages)
- name: Cache opam
uses: actions/cache@v5.0.5
uses: actions/cache@v6.0.0
with:
path: ~/.opam
key: ${{ runner.os }}-opam-${{ matrix.ocaml-version }}-${{ github.sha }}

View file

@ -31,14 +31,14 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -91,7 +91,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -152,7 +152,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -374,7 +374,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -401,7 +401,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout c3 branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 1
persist-credentials: false
@ -990,7 +990,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1183,7 +1183,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1211,7 +1211,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1441,7 +1441,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

57
.github/workflows/pyodide-pypi.yml vendored Normal file
View file

@ -0,0 +1,57 @@
name: Pyodide Wheel (PyPI)
# Builds a PEP 783 `pyemscripten_*_wasm32` wheel for z3-solver using cibuildwheel
# and publishes it to PyPI on tag pushes. Unlike the legacy pyodide.yml (which
# uses `pyodide build` and produces a Pyodide-version-locked emscripten_* wheel
# uploaded only as a CI artifact), this wheel is installable at runtime with
# micropip from PyPI. See src/api/python/pyproject.toml [tool.cibuildwheel].
on:
release:
types: [created]
workflow_dispatch:
permissions:
contents: read
jobs:
build-pyodide:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7.0.0
- name: Build Pyodide wheel
uses: pypa/cibuildwheel@v4.1.0
with:
# The Python bindings live in a subdirectory of the repo.
package-dir: src/api/python
env:
CIBW_PLATFORM: pyodide
# Exception/longjmp/bigint flags are declared in
# src/api/python/pyproject.toml ([tool.pyodide.build]) and combined
# with Pyodide's -fwasm-exceptions defaults. Don't set CFLAGS/CXXFLAGS
# here — a JS-EH -fexceptions value conflicts with the wasm-EH ABI.
- name: Store Pyodide wheel
uses: actions/upload-artifact@v7
with:
name: pyodide-wheel
path: wheelhouse/*.whl
publish:
name: Publish to PyPI
runs-on: ubuntu-24.04
if: startsWith(github.ref, 'refs/tags/')
needs: [build-pyodide]
environment: release
permissions:
id-token: write # trusted publishing (OIDC), no API token needed
steps:
- name: Download Pyodide wheel
uses: actions/download-artifact@v8
with:
name: pyodide-wheel
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

View file

@ -1,68 +0,0 @@
name: Pyodide Build
on:
schedule:
- cron: '0 0 */2 * *'
workflow_dispatch:
env:
BUILD_TYPE: Release
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
- name: Setup packages
run: sudo apt-get update && sudo apt-get install -y python3-dev python3-pip python3-venv
- name: Create venv
run: python3 -m venv ~/env
- name: Install pyodide
run: ~/env/bin/pip install pyodide-build pyodide-cli
- name: Configure CMake and build
run: |
git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
cd ~/emsdk && PYODIDE_EMSCRIPTEN_VERSION=$(~/env/bin/pyodide config get emscripten_version)
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
- name: Build Z3
run: |
source ~/emsdk/emsdk_env.sh
cd src/api/python
CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" CXXFLAG="${CXXFLAGS}" ~/env/bin/pyodide build --exports whole_archive
env:
CFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0 -g2"
LDFLAGS: "-fexceptions -s WASM_BIGINT"
CXXFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0"
- name: Setup env-pyodide
run: |
source ~/env/bin/activate
source ~/emsdk/emsdk_env.sh
pyodide venv ~/env-pyodide
- name: Setup z3 wheel
run: |
~/env-pyodide/bin/pip install src/api/python/dist/*.whl
~/env-pyodide/bin/python - <src/api/python/z3test.py z3
- name: Package wheel
uses: actions/upload-artifact@master
with:
name: pyodide-wheel
path: src/api/python/dist/*.whl
retention-days: 1

View file

@ -31,14 +31,14 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -91,7 +91,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -152,7 +152,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -378,7 +378,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -405,7 +405,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout c3 branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 1
persist-credentials: false
@ -994,7 +994,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1187,7 +1187,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1215,7 +1215,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1445,7 +1445,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -31,14 +31,14 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -92,7 +92,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -153,7 +153,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -378,7 +378,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -405,7 +405,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
persist-credentials: false
@ -993,7 +993,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1185,7 +1185,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1213,7 +1213,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1443,7 +1443,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -36,7 +36,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -78,7 +78,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -122,7 +122,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS x64 Build
uses: actions/download-artifact@v8.0.1
@ -181,7 +181,7 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 Build
uses: actions/download-artifact@v8.0.1
@ -239,7 +239,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -268,7 +268,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -303,7 +303,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -359,7 +359,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Select Python
run: |
@ -397,7 +397,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download ARM toolchain
run: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-linux-gnu.tar.xz'
@ -445,7 +445,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download RISC-V toolchain
run: curl -L -o /tmp/riscv-toolchain.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.09.03/riscv64-glibc-ubuntu-20.04-gcc-nightly-2024.09.03-nightly.tar.gz'
@ -493,66 +493,13 @@ jobs:
path: src/api/python/wheelhouse/*.whl
retention-days: 7
pyodide-python:
name: "Python bindings (Pyodide)"
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
- name: Setup packages
run: sudo apt-get update && sudo apt-get install -y python3-dev python3-pip python3-venv
- name: Create venv
run: python3 -m venv ~/env
- name: Install pyodide
run: ~/env/bin/pip install pyodide-build pyodide-cli
- name: Configure Emscripten
run: |
git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
cd ~/emsdk
PYODIDE_EMSCRIPTEN_VERSION=$(~/env/bin/pyodide config get emscripten_version)
./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
- name: Build wheel
run: |
source ~/emsdk/emsdk_env.sh
cd src/api/python
CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" CXXFLAGS="${CXXFLAGS}" ~/env/bin/pyodide build --exports whole_archive
env:
CFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0 -g2"
LDFLAGS: "-fexceptions -s WASM_BIGINT"
CXXFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0"
- name: Setup env-pyodide
run: |
source ~/env/bin/activate
source ~/emsdk/emsdk_env.sh
pyodide venv ~/env-pyodide
- name: Test wheel
run: |
~/env-pyodide/bin/pip install src/api/python/dist/*.whl
~/env-pyodide/bin/python src/api/python/z3test.py z3
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: PyodidePythonBuild
path: src/api/python/dist/*.whl
retention-days: 7
windows-build-x64:
name: "Windows x64 build"
runs-on: windows-latest
timeout-minutes: 120
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -562,7 +509,8 @@ jobs:
- name: Build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x64 || exit /b 1
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
@ -578,7 +526,7 @@ jobs:
timeout-minutes: 120
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -588,7 +536,8 @@ jobs:
- name: Build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" x86 || exit /b 1
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
@ -604,7 +553,7 @@ jobs:
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -614,7 +563,8 @@ jobs:
- name: Build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
for /f "usebackq delims=" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do set "VSPATH=%%i"
call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64 || exit /b 1
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ env.RELEASE_VERSION }} --zip
- name: Upload artifact
@ -634,7 +584,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -709,7 +659,7 @@ jobs:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -750,11 +700,11 @@ jobs:
python-package:
name: "Python packaging"
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64, pyodide-python]
needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64]
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup Python
uses: actions/setup-python@v6
@ -809,12 +759,6 @@ jobs:
name: ManyLinuxPythonBuildRiscv64
path: artifacts
- name: Download Pyodide Build
uses: actions/download-artifact@v8.0.1
with:
name: PyodidePythonBuild
path: artifacts
- name: Extract builds
run: |
cd artifacts
@ -873,7 +817,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download all artifacts
uses: actions/download-artifact@v8.0.1
@ -929,7 +873,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Download NuGet packages
uses: actions/download-artifact@v8.0.1

View file

@ -31,15 +31,15 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -92,7 +92,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -153,7 +153,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -385,7 +385,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -406,7 +406,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Create gh-aw temp directory
@ -419,7 +419,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1036,7 +1036,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1230,7 +1230,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1258,7 +1258,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1488,7 +1488,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1564,7 +1564,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -31,16 +31,16 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -91,7 +91,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -152,7 +152,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -383,7 +383,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -410,7 +410,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout c3 branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
ref: c3
@ -460,7 +460,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1073,7 +1073,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1267,7 +1267,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1295,7 +1295,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1525,7 +1525,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1601,7 +1601,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -31,16 +31,16 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -94,7 +94,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -155,7 +155,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -386,7 +386,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -413,7 +413,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@ -421,7 +421,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1043,7 +1043,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1234,7 +1234,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1262,7 +1262,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1493,7 +1493,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1569,7 +1569,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -31,14 +31,14 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -91,7 +91,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -152,7 +152,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -379,7 +379,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -406,7 +406,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install build dependencies
@ -999,7 +999,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1192,7 +1192,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1220,7 +1220,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1450,7 +1450,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup node
uses: actions/setup-node@v6

View file

@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.3
uses: actions/checkout@v7.0.0
- name: Setup node
uses: actions/setup-node@v6

View file

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.3
- uses: actions/checkout@v7.0.0
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}

View file

@ -31,16 +31,16 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -94,7 +94,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -155,7 +155,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -387,7 +387,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -414,7 +414,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@ -422,7 +422,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1035,7 +1035,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1228,7 +1228,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1256,7 +1256,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1486,7 +1486,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1562,7 +1562,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

View file

@ -31,16 +31,16 @@
# - GITHUB_TOKEN
#
# Custom actions used:
# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw-actions/setup@v0.79.6
# - github/gh-aw-actions/setup@v0.80.4
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -93,7 +93,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -154,7 +154,7 @@ jobs:
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
sparse-checkout: |
@ -383,7 +383,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -410,7 +410,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
@ -418,7 +418,7 @@ jobs:
- name: Create cache-memory directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- name: Restore cache-memory file share data
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
with:
key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
path: /tmp/gh-aw/cache-memory
@ -1063,7 +1063,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1255,7 +1255,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1283,7 +1283,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
# --- Threat Detection ---
@ -1514,7 +1514,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
@ -1590,7 +1590,7 @@ jobs:
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw-actions/setup@v0.79.6
uses: github/gh-aw-actions/setup@v0.80.4
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}

3
.gitignore vendored
View file

@ -1,9 +1,12 @@
*~
rebase.cmd
reports/
crashes/
*.pyc
*.pyo
# Ignore callgrind files
callgrind.out.*
gmon.out
# .hpp files are automatically generated
*.hpp
.env

View file

@ -19,14 +19,23 @@ filegroup(
cmake(
name = "z3_dynamic",
generate_args = [
"-G Ninja",
"-G Ninja",
"-D Z3_EXPORTED_TARGETS=", # prevents installation, leaving symlinks between dylibs intact on copy
],
lib_source = ":all_files",
out_binaries = ["z3"],
out_shared_libs = select({
"@platforms//os:linux": ["libz3.so"],
# "@platforms//os:osx": ["libz3.dylib"], # FIXME: this is not working, libz3<version>.dylib is not copied
# NOTE: These will need to be manually bumped along side the version in MODULE.bazel/VERSION.txt/CMake
"@platforms//os:linux": [
"libz3.so",
"libz3.so.4.17",
"libz3.so.4.17.0.0",
],
"@platforms//os:osx": [
"libz3.dylib",
"libz3.4.17.dylib",
"libz3.4.17.0.0.dylib",
],
"@platforms//os:windows": ["libz3.dll"],
"//conditions:default": ["@platforms//:incompatible"],
}),
@ -36,7 +45,7 @@ cmake(
cmake(
name = "z3_static",
generate_args = [
"-G Ninja",
"-G Ninja",
"-D BUILD_SHARED_LIBS=OFF",
"-D Z3_BUILD_LIBZ3_SHARED=OFF",
],

View file

@ -204,7 +204,12 @@ elseif (EMSCRIPTEN)
"-Os"
"-s ALLOW_MEMORY_GROWTH=1"
"-s ASSERTIONS=0"
"-s DISABLE_EXCEPTION_CATCHING=0"
# Use native wasm exception handling + wasm longjmp to match the ABI of the
# Pyodide / modern-emscripten main module. The legacy JS-based EH (which the
# removed "-s DISABLE_EXCEPTION_CATCHING=0" selected) makes libz3 import
# invoke_* trampolines the Pyodide runtime no longer provides.
"-fwasm-exceptions"
"-s SUPPORT_LONGJMP=wasm"
"-s ERROR_ON_UNDEFINED_SYMBOLS=1"
)
endif()

View file

@ -23,13 +23,13 @@ See the [release notes](RELEASE_NOTES.md) for notes on various stable releases o
| [![WASM Build](https://github.com/Z3Prover/z3/actions/workflows/wasm.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wasm.yml) | [![Windows](https://github.com/Z3Prover/z3/actions/workflows/Windows.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/Windows.yml) | [![CI](https://github.com/Z3Prover/z3/actions/workflows/ci.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/ci.yml) | [![OCaml Binding CI](https://github.com/Z3Prover/z3/actions/workflows/ocaml.yaml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/ocaml.yaml) |
### Scheduled Workflows
| Open Bugs | Android Build | Pyodide Build | Nightly Build | Cross Build |
| Open Bugs | Android Build | Pyodide Wheel (PyPI) | Nightly Build | Cross Build |
| -----------|---------------|---------------|---------------|-------------|
| [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) | [![Android Build](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml) | [![Pyodide Build](https://github.com/Z3Prover/z3/actions/workflows/pyodide.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/pyodide.yml) | [![Nightly Build](https://github.com/Z3Prover/z3/actions/workflows/nightly.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/nightly.yml) | [![RISC V and PowerPC 64](https://github.com/Z3Prover/z3/actions/workflows/cross-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/cross-build.yml) |
| [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) | [![Android Build](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml) | [![Pyodide Wheel (PyPI)](https://github.com/Z3Prover/z3/actions/workflows/pyodide-pypi.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/pyodide-pypi.yml) | [![Nightly Build](https://github.com/Z3Prover/z3/actions/workflows/nightly.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/nightly.yml) | [![RISC V and PowerPC 64](https://github.com/Z3Prover/z3/actions/workflows/cross-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/cross-build.yml) |
| MSVC Static | MSVC Clang-CL | Build Z3 Cache | Code Coverage | Memory Safety | Mark PRs Ready |
|-------------|---------------|----------------|---------------|---------------|----------------|
| [![MSVC Static Build](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build.yml) | [![MSVC Clang-CL Static Build](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build-clang-cl.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build-clang-cl.yml) | [![Build and Cache Z3](https://github.com/Z3Prover/z3/actions/workflows/build-z3-cache.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/build-z3-cache.yml) | [![Code Coverage](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [![Memory Safety Analysis](https://github.com/Z3Prover/z3/actions/workflows/memory-safety.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/memory-safety.yml) | [![Mark PRs Ready for Review](https://github.com/Z3Prover/z3/actions/workflows/mark-prs-ready-for-review.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/mark-prs-ready-for-review.yml) |
| MSVC Static | MSVC Clang-CL | Build Z3 Cache | Memory Safety | Mark PRs Ready |
|-------------|---------------|----------------|---------------|----------------|
| [![MSVC Static Build](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build.yml) | [![MSVC Clang-CL Static Build](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build-clang-cl.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/msvc-static-build-clang-cl.yml) | [![Build and Cache Z3](https://github.com/Z3Prover/z3/actions/workflows/build-z3-cache.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/build-z3-cache.yml) | [![Memory Safety Analysis](https://github.com/Z3Prover/z3/actions/workflows/memory-safety.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/memory-safety.yml) | [![Mark PRs Ready for Review](https://github.com/Z3Prover/z3/actions/workflows/mark-prs-ready-for-review.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/mark-prs-ready-for-review.yml) |
### Manual & Release Workflows
| Documentation | Release Build | WASM Release | NuGet Build |
@ -42,17 +42,17 @@ See the [release notes](RELEASE_NOTES.md) for notes on various stable releases o
| [![Nightly Build Validation](https://github.com/Z3Prover/z3/actions/workflows/nightly-validation.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/nightly-validation.yml) | [![Copilot Setup Steps](https://github.com/Z3Prover/z3/actions/workflows/copilot-setup-steps.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/copilot-setup-steps.yml) | [![Agentics Maintenance](https://github.com/Z3Prover/z3/actions/workflows/agentics-maintenance.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/agentics-maintenance.yml) |
### Agentic Workflows
| A3 Python | API Coherence | Code Simplifier | Release Notes | Workflow Suggestion |
| ----------|---------------|-----------------|---------------|---------------------|
| [![A3 Python Code Analysis](https://github.com/Z3Prover/z3/actions/workflows/a3-python.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/a3-python.lock.yml) | [![API Coherence Checker](https://github.com/Z3Prover/z3/actions/workflows/api-coherence-checker.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/api-coherence-checker.lock.yml) | [![Code Simplifier](https://github.com/Z3Prover/z3/actions/workflows/code-simplifier.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/code-simplifier.lock.yml) | [![Release Notes Updater](https://github.com/Z3Prover/z3/actions/workflows/release-notes-updater.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/release-notes-updater.lock.yml) | [![Workflow Suggestion Agent](https://github.com/Z3Prover/z3/actions/workflows/workflow-suggestion-agent.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/workflow-suggestion-agent.lock.yml) |
| API Coherence | Code Simplifier | Release Notes | Workflow Suggestion | Academic Citation |
| -------------|-----------------|---------------|---------------------|-------------------|
| [![API Coherence Checker](https://github.com/Z3Prover/z3/actions/workflows/api-coherence-checker.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/api-coherence-checker.lock.yml) | [![Code Simplifier](https://github.com/Z3Prover/z3/actions/workflows/code-simplifier.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/code-simplifier.lock.yml) | [![Release Notes Updater](https://github.com/Z3Prover/z3/actions/workflows/release-notes-updater.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/release-notes-updater.lock.yml) | [![Workflow Suggestion Agent](https://github.com/Z3Prover/z3/actions/workflows/workflow-suggestion-agent.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/workflow-suggestion-agent.lock.yml) | [![Academic Citation Tracker](https://github.com/Z3Prover/z3/actions/workflows/academic-citation-tracker.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/academic-citation-tracker.lock.yml) |
| Academic Citation | Build Warning Fixer | Code Conventions | CSA Report | Issue Backlog |
| ------------------|---------------------|------------------|------------|---------------|
| [![Academic Citation Tracker](https://github.com/Z3Prover/z3/actions/workflows/academic-citation-tracker.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/academic-citation-tracker.lock.yml) | [![Build Warning Fixer](https://github.com/Z3Prover/z3/actions/workflows/build-warning-fixer.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/build-warning-fixer.lock.yml) | [![Code Conventions Analyzer](https://github.com/Z3Prover/z3/actions/workflows/code-conventions-analyzer.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/code-conventions-analyzer.lock.yml) | [![Clang Static Analyzer Report](https://github.com/Z3Prover/z3/actions/workflows/csa-analysis.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/csa-analysis.lock.yml) | [![Issue Backlog Processor](https://github.com/Z3Prover/z3/actions/workflows/issue-backlog-processor.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/issue-backlog-processor.lock.yml) |
| Issue Backlog | Memory Safety Report | QF-S Benchmark | Specbot Crash Analyzer | SMTLIB Benchmark Finder |
| --------------|----------------------|----------------|------------------------|-------------------------|
| [![Issue Backlog Processor](https://github.com/Z3Prover/z3/actions/workflows/issue-backlog-processor.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/issue-backlog-processor.lock.yml) | [![Memory Safety Report](https://github.com/Z3Prover/z3/actions/workflows/memory-safety-report.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/memory-safety-report.lock.yml) | [![ZIPT String Solver Benchmark](https://github.com/Z3Prover/z3/actions/workflows/qf-s-benchmark.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/qf-s-benchmark.lock.yml) | [![Specbot Crash Analyzer](https://github.com/Z3Prover/z3/actions/workflows/specbot-crash-analyzer.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/specbot-crash-analyzer.lock.yml) | [![SMTLIB Benchmark Finder](https://github.com/Z3Prover/z3/actions/workflows/smtlib-benchmark-finder.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/smtlib-benchmark-finder.lock.yml) |
| Memory Safety Report | Ostrich Benchmark | QF-S Benchmark | Tactic-to-Simplifier | ZIPT Code Reviewer |
| ---------------------|-------------------|----------------|----------------------|--------------------|
| [![Memory Safety Report](https://github.com/Z3Prover/z3/actions/workflows/memory-safety-report.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/memory-safety-report.lock.yml) | [![Ostrich Benchmark](https://github.com/Z3Prover/z3/actions/workflows/ostrich-benchmark.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/ostrich-benchmark.lock.yml) | [![ZIPT String Solver Benchmark](https://github.com/Z3Prover/z3/actions/workflows/qf-s-benchmark.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/qf-s-benchmark.lock.yml) | [![Tactic-to-Simplifier](https://github.com/Z3Prover/z3/actions/workflows/tactic-to-simplifier.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/tactic-to-simplifier.lock.yml) | [![ZIPT Code Reviewer](https://github.com/Z3Prover/z3/actions/workflows/zipt-code-reviewer.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/zipt-code-reviewer.lock.yml) |
| TPTP Benchmark |
|----------------|
| [![TPTP Front-End Benchmark](https://github.com/Z3Prover/z3/actions/workflows/tptp-benchmark.lock.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/tptp-benchmark.lock.yml) |
[1]: #building-z3-on-windows-using-visual-studio-command-prompt
[2]: #building-z3-using-make-and-gccclang
@ -299,4 +299,3 @@ to Z3's C API. For more information, see [MachineArithmetic/README.md](https://g
## Power Tools
* The [Axiom Profiler](https://github.com/viperproject/axiom-profiler-2) currently developed by ETH Zurich

View file

@ -1,3 +1,8 @@
if (EMSCRIPTEN OR Z3_SINGLE_THREADED)
# Emscripten (Pyodide/WASM) and single-threaded builds do not use
# libatomic; skip the check to avoid a spurious configure failure.
message(STATUS "Skipping std::atomic link check (EMSCRIPTEN or Z3_SINGLE_THREADED)")
else()
set(ATOMIC_TEST_SOURCE "
#include <atomic>
std::atomic<int> x;
@ -21,3 +26,4 @@ if (NOT BUILTIN_ATOMIC)
message(FATAL_ERROR "Host compiler must support std::atomic!")
endif()
endif()
endif()

View file

@ -20,6 +20,11 @@ set(CLANG_ONLY_WARNINGS
"-Wno-c++98-compat"
"-Wno-c++98-compat-pedantic"
"-Wno-zero-length-array"
"-Wc99-extensions"
"-Wsuggest-override"
"-Winconsistent-missing-override"
"-Wno-missing-field-initializers"
"-Wcast-qual"
)
set(MSVC_WARNINGS "/W3")

BIN
gmon.out

Binary file not shown.

View file

@ -1804,6 +1804,7 @@ class DotNetDLLComponent(Component):
<Description>Z3 is a satisfiability modulo theories solver from Microsoft Research.</Description>
<Copyright>Copyright Microsoft Corporation. All rights reserved.</Copyright>
<PackageTags>smt constraint solver theorem prover</PackageTags>
<PlatformTarget>AnyCPU</PlatformTarget>
%s
</PropertyGroup>

View file

@ -18,13 +18,13 @@
double ackr_helper::calculate_lemma_bound(fun2terms_map const& occs1, sel2terms_map const& occs2) {
double total = 0;
for (auto const& kv : occs1) {
total += n_choose_2_chk(kv.m_value->var_args.size());
total += kv.m_value->const_args.size() * kv.m_value->var_args.size();
for (auto const &[k, v] : occs1) {
total += n_choose_2_chk(v->var_args.size());
total += v->const_args.size() * v->var_args.size();
}
for (auto const& kv : occs2) {
total += n_choose_2_chk(kv.m_value->var_args.size());
total += kv.m_value->const_args.size() * kv.m_value->var_args.size();
for (auto const &[k, v] : occs2) {
total += n_choose_2_chk(v->var_args.size());
total += v->const_args.size() * v->var_args.size();
}
return total;
}

View file

@ -52,14 +52,26 @@ public:
return m_autil.is_select(a) && is_uninterp_const(a->get_arg(0));
}
void mark_non_select_rec(expr* t, expr_mark& visited, expr_mark& non_select) {
if (visited.is_marked(t))
return;
visited.mark(t, true);
non_select.mark(t, true);
if (is_app(t)) {
for (expr *arg : *to_app(t))
mark_non_select_rec(arg, visited,non_select);
}
}
void mark_non_select(app* a, expr_mark& non_select) {
if (m_autil.is_select(a)) {
bool first = true;
expr_mark visited;
for (expr* arg : *a) {
if (first)
first = false;
else
non_select.mark(arg, true);
mark_non_select_rec(arg, visited, non_select);
}
}
else {
@ -70,10 +82,10 @@ public:
void prune_non_select(obj_map<app, app_set*> & sels, expr_mark& non_select) {
ptr_vector<app> nons;
for (auto& kv : sels) {
if (non_select.is_marked(kv.m_key)) {
nons.push_back(kv.m_key);
dealloc(kv.m_value);
for (auto &[k, v] : sels) {
if (non_select.is_marked(k)) {
nons.push_back(k);
dealloc(v);
}
}
for (app* s : nons) {

View file

@ -149,9 +149,9 @@ void lackr::eager_enc() {
checkpoint();
ackr(v);
}
for (auto const& kv : m_sel2terms) {
for (auto const &[k, v] : m_sel2terms) {
checkpoint();
ackr(kv.get_value());
ackr(v);
}
}
@ -190,13 +190,13 @@ void lackr::abstract_fun(fun2terms_map const& apps) {
}
void lackr::abstract_sel(sel2terms_map const& apps) {
for (auto const& kv : apps) {
func_decl * fd = kv.m_key->get_decl();
for (app * t : kv.m_value->const_args) {
for (auto const &[k, v] : apps) {
func_decl * fd = k->get_decl();
for (app * t : v->const_args) {
app * fc = m.mk_fresh_const(fd->get_name(), t->get_sort());
m_info->set_abstr(t, fc);
}
for (app * t : kv.m_value->var_args) {
for (app * t : v->var_args) {
app * fc = m.mk_fresh_const(fd->get_name(), t->get_sort());
m_info->set_abstr(t, fc);
}

View file

@ -60,6 +60,7 @@
<Warn>4</Warn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DocumentationFile>$(OutputPath)\Microsoft.Z3.xml</DocumentationFile>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<!-- Compilation items -->

View file

@ -310,7 +310,7 @@ go run basic_example.go
## Memory Management
The Go bindings use `runtime.SetFinalizer` to automatically manage Z3 reference counts. You don't need to manually call inc_ref/dec_ref. However, be aware that finalizers run during garbage collection, so resources may not be freed immediately.
The Go bindings use `runtime.SetFinalizer` to automatically manage Z3 reference counts. You don't need to manually call inc_ref/dec_ref. However, be aware that finalizers run during garbage collection, so resources may not be freed immediately. The bindings enable `Z3_enable_concurrent_dec_ref` when creating contexts so finalizer-driven decref operations are safe with concurrent GC.
## Thread Safety

View file

@ -89,6 +89,7 @@ type Context struct {
// NewContext creates a new Z3 context with default configuration.
func NewContext() *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(C.Z3_mk_config())}
C.Z3_enable_concurrent_dec_ref(ctx.ptr)
runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr)
})
@ -98,6 +99,7 @@ func NewContext() *Context {
// NewContextWithConfig creates a new Z3 context with the given configuration.
func NewContextWithConfig(cfg *Config) *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(cfg.ptr)}
C.Z3_enable_concurrent_dec_ref(ctx.ptr)
runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr)
})

View file

@ -154,7 +154,7 @@ static void decide_eh(void* _p, Z3_solver_callback cb, Z3_ast _val, unsigned bit
info->jenv->CallVoidMethod(info->jobj, info->decide, (jlong)_val, bit, is_pos);
}
static jboolean on_binding_eh(void* _p, Z3_solver_callback cb, Z3_ast _q, Z3_ast _inst) {
[[maybe_unused]] static jboolean on_binding_eh(void* _p, Z3_solver_callback cb, Z3_ast _q, Z3_ast _inst) {
JavaInfo *info = static_cast<JavaInfo*>(_p);
ScopedCB scoped(info, cb);
return info->jenv->CallBooleanMethod(info->jobj, info->on_binding, (jlong)_q, (jlong)_inst);

View file

@ -5400,10 +5400,20 @@
"dev": true
},
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz",
"integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/markdown-it"
}
],
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
@ -5501,15 +5511,25 @@
}
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz",
"integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/markdown-it"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"linkify-it": "^5.0.1",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"

View file

@ -1,3 +1,29 @@
[build-system]
requires = ["setuptools>=70"]
build-backend = "setuptools.build_meta"
# --- Pyodide / WebAssembly (PEP 783) build configuration ---------------------
# Consumed by pyodide-build (invoked directly or via `cibuildwheel --platform
# pyodide`). These flags are forwarded to the emscripten toolchain that compiles
# libz3 to wasm32. setup.py's IS_PYODIDE branch appends the same -fexceptions
# flags defensively, but declaring them here is what cibuildwheel relies on.
# Pyodide 314 / emscripten 5 builds its main module with *native wasm*
# exception handling and wasm longjmp (see Pyodide's Makefile.envs). Side
# modules like libz3.so MUST match that ABI: building with the legacy
# `-fexceptions` (JS-based EH) makes libz3 import `invoke_*` trampolines that the
# Pyodide runtime no longer provides -> "cannot resolve symbol invoke_vi" at the
# first Z3 call. WASM_BIGINT is required because the Z3 C API passes 64-bit ints
# across the ctypes/JS boundary.
[tool.pyodide.build]
cflags = "-fwasm-exceptions -sSUPPORT_LONGJMP=wasm"
cxxflags = "-fwasm-exceptions -sSUPPORT_LONGJMP=wasm"
ldflags = "-fwasm-exceptions -sSUPPORT_LONGJMP=wasm -sWASM_BIGINT -sSIDE_MODULE=1"
# --- cibuildwheel: produce a PyPI-publishable pyemscripten wheel -------------
[tool.cibuildwheel]
# Pyodide 314 ships CPython 3.14; match the single ABI it targets.
build = "cp314-*"
[tool.cibuildwheel.pyodide]
# z3test.py is the upstream smoke test; run it inside the Pyodide test venv.
test-command = "python {project}/z3test.py z3"

View file

@ -42,9 +42,16 @@ if RELEASE_DIR is None:
BUILD_PLATFORM = "emscripten"
BUILD_ARCH = "wasm32"
BUILD_OS_VERSION = os.environ['_PYTHON_HOST_PLATFORM'].split('_')[1:-1]
build_env['CFLAGS'] = build_env.get('CFLAGS', '') + " -fexceptions"
build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + " -fexceptions"
build_env['LDFLAGS'] = build_env.get('LDFLAGS', '') + " -fexceptions"
# Match Pyodide's native-wasm exception/longjmp ABI (see Makefile.envs in
# Pyodide). The legacy JS-based "-fexceptions" makes libz3.so import
# invoke_* trampolines that the modern Pyodide runtime does not export,
# which surfaces as "cannot resolve symbol invoke_vi" on the first Z3
# call. These mirror [tool.pyodide.build] in pyproject.toml so direct
# `pyodide build` invocations stay consistent with cibuildwheel.
_wasm_eh = " -fwasm-exceptions -sSUPPORT_LONGJMP=wasm"
build_env['CFLAGS'] = build_env.get('CFLAGS', '') + _wasm_eh
build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + _wasm_eh
build_env['LDFLAGS'] = build_env.get('LDFLAGS', '') + _wasm_eh + " -sWASM_BIGINT -sSIDE_MODULE=1"
IS_SINGLE_THREADED = True
ENABLE_LTO = False
# build with pthread doesn't work. The WASM bindings are also single threaded.
@ -305,6 +312,21 @@ class bdist_wheel(_bdist_wheel):
def finalize_options(self):
if BUILD_PLATFORM == "emscripten":
# Under pyodide-build / `cibuildwheel --platform pyodide`, the
# authoritative wheel platform tag is handed to us verbatim via
# _PYTHON_HOST_PLATFORM. For PEP 783 (Pyodide >= 0.28 / "314") this
# is e.g. "pyemscripten_2026_0_wasm32" -- a tag PyPI accepts. The
# reconstruction below instead produced "emscripten_<ver>_wasm32",
# which is locked to a Pyodide release and rejected by PyPI, so we
# defer to pyodide-build's tag when it is available.
host_platform = os.environ.get('_PYTHON_HOST_PLATFORM')
if host_platform:
self.plat_name = host_platform
else:
os_version_tag = '_'.join(BUILD_OS_VERSION) if BUILD_OS_VERSION else 'xxxxxx'
self.plat_name = f"emscripten_{os_version_tag}_wasm32"
return super().finalize_options()
if BUILD_ARCH is not None and BUILD_PLATFORM is not None:
os_version_tag = '_'.join(BUILD_OS_VERSION) if BUILD_OS_VERSION is not None else 'xxxxxx'
os_version_tag = self.remove_build_machine_os_version(BUILD_PLATFORM, os_version_tag)

View file

@ -621,7 +621,7 @@ struct z3_replayer::imp {
Z3_symbol get_symbol(unsigned pos) const {
check_arg(pos, SYMBOL);
return (Z3_symbol)m_args[pos].m_sym;
return (Z3_symbol)const_cast<void*>(m_args[pos].m_sym);
}
void * get_obj(unsigned pos) const {

View file

@ -2894,7 +2894,7 @@ proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs
}
});
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_eq(n1,n2));
return mk_app(basic_family_id, PR_TRANSITIVITY_STAR, args.size(), args.data());
}
@ -2903,7 +2903,7 @@ proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned
SASSERT(f1->get_num_args() == f2->get_num_args());
SASSERT(f1->get_decl() == f2->get_decl());
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_app(R, f1, f2));
proof* p = mk_app(basic_family_id, PR_MONOTONICITY, args.size(), args.data());
return p;
@ -2965,7 +2965,7 @@ proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, pr
if (proofs_disabled())
return nullptr;
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_eq(s, t));
return mk_app(basic_family_id, PR_REWRITE_STAR, args.size(), args.data());
}
@ -3055,7 +3055,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
}
if (!found_complement) {
args.append(num_proofs, (expr**)proofs);
args.append(num_proofs, (expr* const *)proofs);
CTRACE(mk_unit_resolution_bug, !is_or(f1), tout << mk_ll_pp(f1, *this) << "\n";
for (unsigned i = 1; i < num_proofs; ++i)
tout << mk_pp(proofs[i], *this) << "\n";
@ -3125,7 +3125,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
tout << mk_pp(new_fact, *this) << "\n";);
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(new_fact);
#ifdef Z3DEBUG
expr * f1 = get_fact(proofs[0]);
@ -3191,7 +3191,7 @@ proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, pr
if (proofs_disabled())
return nullptr;
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_oeq(n, def));
return mk_app(basic_family_id, PR_APPLY_DEF, args.size(), args.data());
}
@ -3225,7 +3225,7 @@ proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof *
return nullptr;
check_nnf_proof_parents(num_proofs, proofs);
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_oeq(s, t));
return mk_app(basic_family_id, PR_NNF_POS, args.size(), args.data());
}
@ -3235,7 +3235,7 @@ proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof *
return nullptr;
check_nnf_proof_parents(num_proofs, proofs);
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_oeq(mk_not(s), t));
return mk_app(basic_family_id, PR_NNF_NEG, args.size(), args.data());
}
@ -3305,7 +3305,7 @@ proof * ast_manager::mk_redundant_del(expr* e) {
proof * ast_manager::mk_clause_trail(unsigned n, expr* const* ps) {
ptr_buffer<expr> args;
args.append(n, (expr**) ps);
args.append(n, (expr* const *) ps);
return mk_app(basic_family_id, PR_CLAUSE_TRAIL, 0, nullptr, args.size(), args.data());
}
@ -3323,7 +3323,7 @@ proof * ast_manager::mk_th_lemma(
for (unsigned i = 0; i < num_params; ++i)
parameters.push_back(params[i]);
ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs);
args.append(num_proofs, (expr* const *) proofs);
args.push_back(fact);
return mk_app(basic_family_id, PR_TH_LEMMA, parameters.size(), parameters.data(), args.size(), args.data());
}

View file

@ -68,8 +68,8 @@ private:
inline ast_manager & m() const { return m_manager; }
// label for an expression
std::string label_of_expr(const expr * e) const {
expr_ref er((expr*)e, m());
std::string label_of_expr(const expr *e) const {
expr_ref er(const_cast<expr *>(e), m());
std::ostringstream out;
out << er << std::flush;
return escape_dot(out.str());

View file

@ -39,11 +39,16 @@ z3_add_component(rewriter
rewriter.cpp
seq_axioms.cpp
seq_eq_solver.cpp
seq_derive.cpp
seq_subset.cpp
seq_split.cpp
seq_derive.cpp
seq_range_collapse.cpp
seq_range_predicate.cpp
seq_rewriter.cpp
seq_regex_bisim.cpp
seq_skolem.cpp
term_enumeration.cpp
th_rewriter.cpp
value_sweep.cpp
var_subst.cpp

View file

@ -768,9 +768,10 @@ void bit_blaster_tpl<Cfg>::mk_smod(unsigned sz, expr * const * a_bits, expr * co
template<typename Cfg>
void bit_blaster_tpl<Cfg>::mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) {
expr_ref_vector out_bits(m());
out_bits.resize(sz);
for (unsigned i = 0; i < sz; ++i) {
mk_iff(a_bits[i], b_bits[i], out);
out_bits.push_back(out);
out_bits[i] = out;
}
mk_and(out_bits.size(), out_bits.data(), out);
}

View file

@ -1196,15 +1196,15 @@ bool bool_rewriter::decompose_ite(expr *r, expr_ref &c, expr_ref &th, expr_ref &
}
for (expr *e : subterms::ground(expr_ref(r, m()))) {
if (m().is_ite(e, cond, r1, r2)) {
expr_safe_replace rep1(m());
expr_safe_replace rep2(m());
rep1.insert(e, r1);
rep2.insert(e, r2);
m_rep1.reset();
m_rep2.reset();
m_rep1.insert(e, r1);
m_rep2.insert(e, r2);
c = cond;
th = r;
el = r;
rep1(th);
rep2(el);
m_rep1(th);
m_rep2(el);
return true;
}
}
@ -1212,6 +1212,4 @@ bool bool_rewriter::decompose_ite(expr *r, expr_ref &c, expr_ref &th, expr_ref &
}
template class rewriter_tpl<bool_rewriter_cfg>;
template class rewriter_tpl<bool_rewriter_cfg>;

View file

@ -20,6 +20,7 @@ Notes:
#include "ast/ast.h"
#include "ast/rewriter/rewriter.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "util/params.h"
/**
@ -64,6 +65,7 @@ class bool_rewriter {
ptr_vector<expr> m_todo1, m_todo2;
unsigned_vector m_counts1, m_counts2;
expr_mark m_marked;
expr_safe_replace m_rep1, m_rep2;
br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result);
@ -87,7 +89,7 @@ class bool_rewriter {
expr_ref simplify_eq_ite(expr* value, expr* ite);
public:
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) {
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0), m_rep1(m), m_rep2(m) {
updt_params(p);
}
ast_manager & m() const { return m_manager; }
@ -243,7 +245,9 @@ public:
void mk_nor(expr * arg1, expr * arg2, expr_ref & result);
void mk_ge2(expr* a, expr* b, expr* c, expr_ref& result);
// If r is, or contains, an if-then-else, decompose it into a top-level
// ite by hoisting the (first) inner ite condition: returns c, th, el such
// that r is equivalent to (ite c th el). Returns false if r has no ite.
bool decompose_ite(expr *r, expr_ref &c, expr_ref &th, expr_ref &el);
};

View file

@ -452,6 +452,8 @@ namespace seq {
// |t| = 0 => |s| = 0 or indexof(t,s,offset) = -1
// ~contains(t,s) => indexof(t,s,offset) = -1
add_clause(mk_ge(i, -1));
add_clause(cnt, i_eq_m1);
add_clause(~t_eq_empty, s_eq_empty, i_eq_m1);
@ -638,8 +640,8 @@ namespace seq {
add_clause(~i_ge_0, i_ge_len_s, mk_eq(i, len_x));
}
add_clause(i_ge_0, mk_seq_eq(e, emp));
add_clause(~i_ge_len_s, mk_seq_eq(e, emp));
add_clause(i_ge_0, mk_eq(e, emp));
add_clause(~i_ge_len_s, mk_eq(e, emp));
add_clause(~i_ge_0, i_ge_len_s, mk_eq(one, len_e));
add_clause(mk_le(len_e, 1));
}
@ -1066,7 +1068,7 @@ namespace seq {
void axioms::replace_re_axiom(expr* e) {
expr* s = nullptr, *r = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_re(e, s, r, t));
throw default_exception("replace-re is not supported");
throw default_exception("no support for replace-re");
}
// A basic strategy for supporting replace_all and other
@ -1075,34 +1077,22 @@ namespace seq {
// using iterative deepening can be re-used.
//
// create recursive relation 'ra' with properties:
// ra(i, j, s, p, t, r) =
// if len(s) = i && len(r) = j then
// true
// else if len(s) > i = 0 && p = "" && r = t + s then
// true
// else if len(s) > i && p != "" &&
// s = extract(s, 0, i) + p + extract(s, i + len(p), len(s)) &&
// r = extract(r, 0, i) + t + extract(r, i + len(p), len(r)) && ra(i + len(p), j + len(t), s, p, t, r)
// else if ~prefix(p, extract(s, i, len(s)) && at(s,i) = at(r,j) then
// ra(i + 1, j + 1, s, p, t, r)
// else false
// ra(i, j, s, p, t, r) <- len(s) = i && len(r) = j
// ra(i, j, s, p, t, r) <- len(s) > i = 0 && p = "" && r = t + s
// ra(i, j, s, p, t, r) <- len(s) > i && p != "" && s = extract(s, 0, i) + p + extract(s, i + len(p), len(s)) && r = extract(r, 0, i) + t + extract(r, i + len(p), len(r)) && ra(i + len(p), j + len(t), s, p, t, r)
// ra(i, s, p, t, r) <- ~prefix(p, extract(s, i, len(s)) && at(s,i) = at(r,j) && ra(i + 1, j + 1, s, p, t, r)
// which amounts to:
//
//
// Then assert
// ra(s, p, t, replace_all(s, p, t))
//
// ra(s, p, t, r) is a recursive predicate:
// ra(s, p, t, r) iff replace_all(s, p, t) = r
//
// Base case, empty s or p: r = s
// Match case, prefix(p, s): s = p ++ s', r = t ++ r', ra(s', p, t, r')
// No-match case: r[0] = s[0], ra(s[1:], p, t, r[1:])
//
// Assert: ra(s, p, t, replace_all(s, p, t))
//
void axioms::replace_all_axiom(expr* r) {
expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_all(r, s, p, t));
recfun::util rec(m);
recfun::decl::plugin& plugin = rec.get_plugin();
recfun_replace replace(m);
sort* srt = s->get_sort();
sort* domain[4] = { srt, srt, srt, srt };
auto ra = rec.find_def_decl(symbol("ra"), 4, domain, m.mk_bool_sort(), true);
@ -1146,7 +1136,7 @@ namespace seq {
void axioms::replace_re_all_axiom(expr* e) {
expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_re_all(e, s, p, t));
throw default_exception("replace_re_all is not supported");
throw default_exception("no support for replace-re-all");
}
@ -1345,7 +1335,7 @@ namespace seq {
/**
* Consider the recursive definition of negated contains:
~contains(a, b) =
~contains(a, b) =
if |b| > |a| then true
else if |b| = |a| then a != b
else ~prefix(b, a) and ~contains(a[1:], b)
@ -1420,9 +1410,9 @@ namespace seq {
return bound_tracker;
}
// |u| != |v| OR
// |u| != |v| OR
// (u = w[a]u' AND v = w[b]v' AND a != b AND |u'| = |v'|)
void axioms::diseq_axiom(expr *u, expr *v) {
void axioms::diseq_axiom(expr *u, expr *v) {
expr_ref u_len(mk_len(u), m);
expr_ref v_len(mk_len(v), m);
expr_ref len_eq(mk_eq(u_len, v_len), m);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,266 @@
/*++
Copyright (c) 2026 Microsoft Corporation
Module Name:
seq_derive.h
Abstract:
Symbolic derivative computation for regular expressions.
Produces an ITE-tree (transition regex) representation where
the free variable is de Bruijn index 0 representing the input character.
Based on the theory of symbolic derivatives and transition regexes:
- Veanes et al., "On Symbolic Derivatives and Transition Regexes" (LPAR 2024)
- Varatalu, Veanes, Ernits, "RE#" (POPL 2025)
- Stanford, Veanes, Bjørner, "Symbolic Boolean Derivatives" (PLDI 2021)
Authors:
Nikolaj Bjorner (nbjorner) 2025-06-03
--*/
#pragma once
#include "ast/seq_decl_plugin.h"
#include "ast/arith_decl_plugin.h"
#include "ast/array_decl_plugin.h"
#include "ast/rewriter/bool_rewriter.h"
#include "util/obj_pair_hashtable.h"
#include "util/obj_triple_hashtable.h"
#include <functional>
class seq_rewriter;
namespace seq {
enum class derivative_kind { antimirov_t, brzozowski_t };
/**
* Symbolic derivative engine for regular expressions.
*
* Given a regex r, operator()(r) computes a symbolic derivative δ(r)
* represented as an ITE-tree over character predicates (using de Bruijn
* variable 0 for the character). Evaluating the ITE-tree for a concrete
* character 'a' yields the classical Brzozowski derivative δ_a(r).
*
* The ITE-tree structure implicitly defines minterms (equivalence classes
* of characters indistinguishable by the regex).
*
* Key properties:
* - Results are memoized for termination on cyclic derivative graphs
* - Union/intersection operands are sorted for ACI canonicalization
* - Depth-bounded to prevent stack overflow
*/
class derive {
ast_manager& m;
seq_util m_util;
arith_util m_autil;
bool_rewriter m_br;
seq_rewriter& m_re;
// Cache: maps (ele, regex) pair to its derivative
obj_pair_map<expr, expr, expr*> m_acache, m_bcache;
obj_pair_map<expr, expr, expr*> m_atop_cache, m_btop_cache; // post-simplify cache
expr_ref_vector m_trail; // pin cached results
// Op cache for ITE-hoisting operations (union, inter, concat, complement)
// Path-aware caches: key is (a, b, path_expr) for binary ops, (a, path_expr) for complement
obj_triple_map<expr, expr, expr, expr *> m_aunion_cache, m_bunion_cache, m_ainter_cache, m_binter_cache, m_axor_cache, m_bxor_cache;
obj_pair_map<expr, expr, expr*> m_aconcat_cache, m_bconcat_cache;
obj_pair_map<expr, expr, expr*> m_acomplement_cache, m_bcomplement_cache;
// Depth limiting
unsigned m_depth { 0 };
static const unsigned m_max_depth = 512;
seq_util::rex& re() { return m_util.re; }
seq_util& u() { return m_util; }
derivative_kind m_derivative_kind = derivative_kind::antimirov_t;
// The element (character) for the current derivative computation
expr_ref m_ele;
// Path state for inline pruning during mk_inter/mk_union/mk_complement
using intervals_t = svector<std::pair<unsigned, unsigned>>;
// Path: vector of signed atoms
svector<std::pair<expr*, bool>> m_path;
// Intervals: feasible character ranges under current path (append-only)
intervals_t m_intervals;
unsigned m_intervals_start { 0 };
// Stack of saved states for push/pop
struct path_save { unsigned path_sz; unsigned intervals_sz; unsigned intervals_start; expr* path_expr; };
svector<path_save> m_path_stack;
// Boolean expression encoding of current path (for cache keys)
expr_ref m_path_expr;
// Path interface
lbool push(expr* c, bool sign); // l_true: implied, l_undef: pushed (must pop), l_false: contradicts
void pop(); // restore state to matching push
expr* get_path_expr() { return m_path_expr; }
obj_pair_map<expr, expr, expr *> &cache() {
return m_derivative_kind == derivative_kind::antimirov_t ? m_acache : m_bcache;
}
obj_pair_map<expr, expr, expr *> &top_cache() {
return m_derivative_kind == derivative_kind::antimirov_t ? m_atop_cache : m_btop_cache;
}
obj_triple_map<expr, expr, expr, expr *> &union_cache() {
return m_derivative_kind == derivative_kind::antimirov_t ? m_aunion_cache : m_bunion_cache;
}
obj_triple_map<expr, expr, expr, expr *> &inter_cache() {
return m_derivative_kind == derivative_kind::antimirov_t ? m_ainter_cache : m_binter_cache;
}
obj_triple_map<expr, expr, expr, expr *> &xor_cache() {
return m_derivative_kind == derivative_kind::antimirov_t ? m_axor_cache : m_bxor_cache;
}
obj_pair_map<expr, expr, expr *> &concat_cache() {
return m_derivative_kind == derivative_kind::antimirov_t ? m_aconcat_cache : m_bconcat_cache;
}
obj_pair_map<expr, expr, expr *> &complement_cache() {
return m_derivative_kind == derivative_kind::antimirov_t ? m_acomplement_cache : m_bcomplement_cache;
}
// Hoist ITE: apply_op through ite(c, t, e) with path pruning
expr_ref apply_ite(expr* c, expr* t, expr* e, expr* r, std::function<expr_ref(expr*, expr*)> apply_op);
expr_ref apply_ite(expr* c, expr* t1, expr* e1, expr* t2, expr* e2, std::function<expr_ref(expr*, expr*)> apply_op);
expr_ref apply_ite(expr* c, expr* t, expr* e, std::function<expr_ref(expr*)> apply_op);
// Common ITE dispatch for binary ops (union/inter)
expr_ref hoist_ite(expr* a, expr* b, std::function<expr_ref(expr*, expr*)> apply_op);
// Evaluate a condition against the current path/intervals
lbool eval_path_cond(expr* c);
// Internal helpers for push
lbool push_path_atoms(expr* c, bool sign);
lbool push_intervals_impl(expr* c, bool sign);
// Core derivative computation
expr_ref derive_rec(expr* r);
expr_ref derive_core(expr* r);
// Helpers for specific regex constructs
expr_ref derive_to_re(expr* s, sort* seq_sort);
expr_ref derive_range(expr* lo, expr* hi, sort* seq_sort);
expr_ref derive_of_pred(expr* pred, sort* seq_sort);
// Nullable check: returns a Boolean expression
expr_ref is_nullable(expr* r);
expr_ref is_nullable_symbolic_regex(expr* r, sort* seq_sort);
// Smart constructors with path-aware simplification and ACI canonicalization
expr_ref mk_union(expr* a, expr* b);
bool are_complements(expr* a, expr* b);
unsigned union_id(expr* e); // complement-aware ID for sorting
bool is_subset(expr* a, expr* b);
expr_ref mk_union_core(expr* a, expr* b);
void add_union_elem(expr_ref_vector& set, expr* e);
expr_ref mk_inter(expr* a, expr* b);
expr_ref mk_inter_core(expr* a, expr* b);
expr_ref mk_concat(expr* a, expr* b);
expr_ref mk_complement(expr* a);
expr_ref mk_complement_core(expr* a);
expr_ref mk_xor(expr *a, expr *b);
expr_ref mk_xor_core(expr *a, expr *b);
expr_ref mk_core(decl_kind k, expr* a, expr* b);
expr_ref mk_ite(expr* c, expr* t, expr* e);
// Distribute concatenation through ITE/union in derivative
expr_ref mk_deriv_concat(expr* d, expr* tail);
expr_ref mk_deriv_concat_core(expr* d, expr* tail);
// Extract head character and tail from a sequence expression
bool get_head_tail(expr* s1, expr* s2, expr_ref& hd, expr_ref& tl);
// Predicate implication for character range conditions.
bool pred_implies(bool sign_a, expr* a, bool sign_b, expr* b);
bool pred_implies(expr* a, expr* b);
// Normalize reverse(r)
expr_ref mk_regex_reverse(expr* r);
// Condition evaluation helpers
lbool eval_cond(expr* cond);
lbool eval_range_cond(expr* c);
void intersect_intervals(unsigned lo, unsigned hi);
void exclude_interval(unsigned lo, unsigned hi);
// Cofactor enumeration over a transition regex (ITE-tree).
void get_cofactors_rec(expr* r, expr_ref_pair_vector& result);
// Re-apply union/intersection simplifications bottom-up to a cofactor
// leaf. decompose_ite substitutes ITE branch values structurally
// (no simplification), so leaves can contain un-normalized nodes such
// as union(R, none) or inter(R, none); this rebuilds them through
// mk_union/mk_inter so equal states share a canonical form.
expr_ref clean_leaf(expr* r);
sort* re_sort(expr* r) { return r->get_sort(); }
sort* seq_sort(expr* r) { sort* s = nullptr; m_util.is_re(r, s); return s; }
sort* ele_sort(expr* r) { sort* s = seq_sort(r); sort* e = nullptr; m_util.is_seq(s, e); return e; }
void reset();
void reset_op_caches();
public:
derive(ast_manager& m, seq_rewriter& re);
/**
* Compute the derivative of regex r with respect to element ele.
* When ele is a de Bruijn variable, produces a symbolic ITE-tree.
* When ele is a concrete character, produces the concrete derivative.
*/
expr_ref operator()(derivative_kind k, expr* ele, expr* r);
/**
* Convenience: symbolic derivative using de Bruijn var 0.
*/
expr_ref operator()(derivative_kind k, expr* r);
/**
* Nullable check: returns a Boolean expression that is true iff r accepts the empty string.
*/
expr_ref nullable(expr* r) { return is_nullable(r); }
/**
* Enumerate the cofactors (min-terms) of a transition regex r taken with
* respect to element ele. r is an ITE-tree over character predicates on
* ele; for every feasible path through the tree this produces a pair
* (path_condition, leaf_regex). Infeasible character-interval
* combinations are pruned using the same path/interval context that the
* derivative engine uses while hoisting ITEs.
*/
void get_cofactors(expr* ele, expr* r, expr_ref_pair_vector& result);
/**
* Compute the symbolic derivative of r and enumerate its reachable
* leaves in fully ITE-hoisted normal form.
*
* Concretely this returns, for every feasible minterm (character
* class) of δ(r), a pair (path_condition, target_regex). Every
* if-then-else over the input character (including ones that would
* otherwise be buried under a concat/union) is hoisted to the top
* via the same path/interval pruning used by the derivative engine,
* so each target_regex is free of (:var 0) and its nullability is
* always decidable. Unions are kept intact as single leaves (a
* union leaf denotes a single bisimulation state). Infeasible
* minterms are pruned, so all returned leaves are reachable.
*
* This is the entry point the regex_bisim equivalence procedure
* uses: it consumes the target_regex of each pair and ignores the
* (redundant) path condition.
*/
void derivative_cofactors(expr* r, expr_ref_pair_vector& result);
};
}

View file

@ -0,0 +1,160 @@
/*++
Copyright (c) 2026 Microsoft Corporation
Module Name:
seq_range_collapse.cpp
Abstract:
Implementation of regex <-> range_predicate translation for the
boolean-combination-of-ranges fragment. See header for the recognized
grammar and the canonical regex AST emitted by materialization.
Authors:
Margus Veanes (veanes) 2026
--*/
#include "ast/rewriter/seq_range_collapse.h"
namespace seq {
bool regex_to_range_predicate(seq_util& u, expr* r, range_predicate& out) {
// The range algebra only models sets of single characters over the
// unsigned character domain [0, max_char]. Guard against any regex
// whose element type is not a sequence of characters (e.g. a regex
// over (Seq Int) or (Seq (Seq Char))): for such regexes the
// re.range/re.union/... matchers below would silently fabricate a
// character-class predicate and change semantics. Reject them up
// front so callers fall back to the generic regex path.
sort* seq_sort = nullptr;
if (!u.is_re(r, seq_sort) || !u.is_string(seq_sort))
return false;
unsigned const max_char = u.max_char();
auto& re = u.re;
if (re.is_empty(r)) {
out = range_predicate::empty(max_char);
return true;
}
if (re.is_full_char(r)) {
out = range_predicate::top(max_char);
return true;
}
unsigned lo = 0, hi = 0;
expr* lo_e = nullptr;
expr* hi_e = nullptr;
if (re.is_range(r, lo_e, hi_e)) {
auto extract_char = [&](expr* e, unsigned& c) -> bool {
if (u.is_const_char(e, c)) return true;
expr* inner = nullptr;
if (u.str.is_unit(e, inner) && u.is_const_char(inner, c)) return true;
zstring s;
if (u.str.is_string(e, s) && s.length() == 1) {
c = s[0];
return true;
}
return false;
};
if (!extract_char(lo_e, lo) || !extract_char(hi_e, hi))
return false;
// Empty/inverted range [lo > hi] is the empty regex.
if (lo > hi) {
out = range_predicate::empty(max_char);
return true;
}
out = range_predicate::range(lo, hi, max_char);
return true;
}
expr *a = nullptr, *b = nullptr, *c = nullptr;
if (re.is_union(r, a, b)) {
range_predicate pa(max_char), pb(max_char);
if (!regex_to_range_predicate(u, a, pa)) return false;
if (!regex_to_range_predicate(u, b, pb)) return false;
out = pa | pb;
return true;
}
auto mk_diff = [&](expr *a, expr *b) -> bool {
range_predicate pa(max_char), pb(max_char);
if (!regex_to_range_predicate(u, a, pa))
return false;
if (!regex_to_range_predicate(u, b, pb))
return false;
out = pa - pb;
return true;
};
if (re.is_diff(r, a, b))
return mk_diff(a, b);
if (re.is_intersection(r, a, b) && re.is_complement(b, c))
return mk_diff(a, c);
if (re.is_intersection(r, a, b) && re.is_complement(a, c))
return mk_diff(b, c);
if (re.is_intersection(r, a, b)) {
range_predicate pa(max_char), pb(max_char);
if (!regex_to_range_predicate(u, a, pa)) return false;
if (!regex_to_range_predicate(u, b, pb)) return false;
out = pa & pb;
return true;
}
// NOTE: re.complement is intentionally NOT handled here.
// re.complement is the SEQUENCE-level complement: its language
// includes the empty string, strings of length >= 2, and any
// length-1 string outside the operand. A character-class
// range_predicate can only describe a set of length-1 strings,
// so collapsing re.complement(R) to ~R (character-level
// complement) would change semantics whenever R is wrapped in
// any sequence-level context (e.g. re.diff at the top level,
// or membership tests). De-Morgan equivalences and the
// special cases re.complement(re.empty) / re.complement(re.full)
// are already handled directly in seq_rewriter::mk_re_complement.
return false;
}
static expr_ref mk_unit_string_from_char(seq_util& u, unsigned c) {
return expr_ref(u.str.mk_string(zstring(c)), u.get_manager());
}
static expr_ref mk_single_range_regex(seq_util& u, unsigned lo, unsigned hi, sort* re_sort) {
ast_manager& m = u.get_manager();
return expr_ref(u.re.mk_range(re_sort, lo, hi), m);
}
expr_ref range_predicate_to_regex(seq_util& u, range_predicate const& p, sort* seq_sort) {
ast_manager& m = u.get_manager();
sort* re_sort = u.re.mk_re(seq_sort);
if (p.is_empty())
return expr_ref(u.re.mk_empty(re_sort), m);
unsigned const n = p.num_ranges();
SASSERT(n > 0);
if (n == 1) {
auto [lo, hi] = p[0];
return mk_single_range_regex(u, lo, hi, re_sort);
}
// Build single-range AST nodes first, then sort by expression id
// so the resulting right-associated union matches the canonical
// id-sorted shape that seq_rewriter::merge_regex_sets expects.
// Without this the merge algorithm produces incorrect unions
// when it has to combine our materialized output with another
// (id-sorted) regex set.
expr_ref_vector ranges(m);
for (unsigned i = 0; i < n; ++i) {
auto [lo, hi] = p[i];
ranges.push_back(mk_single_range_regex(u, lo, hi, re_sort));
}
std::sort(ranges.data(), ranges.data() + ranges.size(),
[](expr* a, expr* b) { return a->get_id() < b->get_id(); });
expr_ref acc(ranges.get(n - 1), m);
for (unsigned i = n - 1; i-- > 0; )
acc = expr_ref(u.re.mk_union(ranges.get(i), acc), m);
return acc;
}
}

View file

@ -0,0 +1,71 @@
/*++
Copyright (c) 2026 Microsoft Corporation
Module Name:
seq_range_collapse.h
Abstract:
Recognize regexes that are boolean combinations of character-class
primitives (re.empty, re.full_char, re.range with concrete chars,
and re.union/inter/comp/diff over translatable arguments), and
materialize a seq::range_predicate back into a canonical regex AST.
Together with seq_rewriter integration, this lets any boolean
combination of character-class regexes collapse to a canonical
multi-range form, so that equivalent character classes share AST
identity, and downstream consumers (derivative, OneStep, caching)
can short-circuit them as pure range predicates.
Authors:
Margus Veanes (veanes) 2026
--*/
#pragma once
#include "ast/rewriter/seq_range_predicate.h"
#include "ast/seq_decl_plugin.h"
namespace seq {
/**
* If r is a boolean combination of character-class regex primitives
* over the unsigned character domain [0, max_char], compute the
* equivalent range_predicate and return true. Otherwise return false
* with out untouched.
*
* Recognized fragment (all character-class-preserving operations):
* re.empty -> empty
* re.full_char_set -> top
* re.range "c_lo" "c_hi" (concrete) -> [c_lo, c_hi]
* re.union r1 r2 -> p1 | p2
* re.intersection r1 r2 -> p1 & p2
* re.diff r1 r2 -> p1 - p2
*
* Notably re.complement is NOT recognized: it is a SEQUENCE-level
* complement (over all of Σ*), not a character-class complement, so
* collapsing it would change semantics whenever the result is used
* in any non-character-class context. Sequence-level rewrites for
* re.complement (double-comp, deMorgan, etc.) are handled directly
* in seq_rewriter::mk_re_complement.
*/
bool regex_to_range_predicate(seq_util& u, expr* r, range_predicate& out);
/**
* Canonical materialization of p as a regex AST over the given
* sequence sort. Two range_predicates with equal canonical
* representations produce structurally identical regex ASTs:
*
* empty -> re.empty
* top -> re.full_char_set
* single range [lo, hi] -> re.range "lo" "hi"
* multiple ranges -> right-associated re.union of single
* ranges, in increasing order of lo
* (matching the canonical range order
* held by range_predicate).
*/
expr_ref range_predicate_to_regex(seq_util& u, range_predicate const& p, sort* seq_sort);
}

View file

@ -0,0 +1,292 @@
/*++
Copyright (c) 2026 Microsoft Corporation
Module Name:
seq_range_predicate.cpp
Abstract:
Implementation of the specialized range-algebra used by symbolic
derivative computation and regex rewriting. See seq_range_predicate.h
for the algebraic specification.
All Boolean operations are implemented as single linear sweeps over
the canonical sorted range vectors and produce canonical output
(sorted, disjoint, non-adjacent).
Authors:
Margus Veanes (veanes) 2026
--*/
#include "ast/rewriter/seq_range_predicate.h"
#include "util/debug.h"
#include <algorithm>
#include <ostream>
namespace seq {
// -----------------------------------------------------------------------
// Factories
// -----------------------------------------------------------------------
range_predicate range_predicate::empty(unsigned max_char) {
return range_predicate(max_char);
}
range_predicate range_predicate::top(unsigned max_char) {
range_predicate r(max_char);
r.m_ranges.push_back({0u, max_char});
SASSERT(r.well_formed());
return r;
}
range_predicate range_predicate::singleton(unsigned c, unsigned max_char) {
SASSERT(c <= max_char);
range_predicate r(max_char);
r.m_ranges.push_back({c, c});
SASSERT(r.well_formed());
return r;
}
range_predicate range_predicate::range(unsigned lo, unsigned hi, unsigned max_char) {
range_predicate r(max_char);
if (lo <= hi && lo <= max_char) {
unsigned clipped_hi = hi <= max_char ? hi : max_char;
r.m_ranges.push_back({lo, clipped_hi});
}
SASSERT(r.well_formed());
return r;
}
// -----------------------------------------------------------------------
// Invariants and observers
// -----------------------------------------------------------------------
bool range_predicate::well_formed() const {
for (unsigned i = 0; i < m_ranges.size(); ++i) {
auto [lo, hi] = m_ranges[i];
if (lo > hi) return false;
if (hi > m_max_char) return false;
if (i > 0) {
unsigned prev_hi = m_ranges[i - 1].second;
// Non-adjacent and sorted: prev_hi + 1 < lo, with care
// around prev_hi == UINT_MAX which we never expect because
// hi <= m_max_char.
if (prev_hi + 1 >= lo) return false;
}
}
return true;
}
bool range_predicate::contains(unsigned c) const {
// Binary search on first element of pairs.
unsigned lo = 0, hi = m_ranges.size();
while (lo < hi) {
unsigned mid = lo + (hi - lo) / 2;
auto [a, b] = m_ranges[mid];
if (c < a) hi = mid;
else if (c > b) lo = mid + 1;
else return true;
}
return false;
}
uint64_t range_predicate::cardinality() const {
uint64_t n = 0;
for (auto [lo, hi] : m_ranges)
n += static_cast<uint64_t>(hi) - static_cast<uint64_t>(lo) + 1u;
return n;
}
// -----------------------------------------------------------------------
// Equality, ordering, hashing
// -----------------------------------------------------------------------
bool range_predicate::equals(range_predicate const& o) const {
if (m_max_char != o.m_max_char) return false;
if (m_ranges.size() != o.m_ranges.size()) return false;
for (unsigned i = 0; i < m_ranges.size(); ++i)
if (m_ranges[i] != o.m_ranges[i]) return false;
return true;
}
bool range_predicate::operator<(range_predicate const& o) const {
if (m_max_char != o.m_max_char)
return m_max_char < o.m_max_char;
unsigned n = std::min(m_ranges.size(), o.m_ranges.size());
for (unsigned i = 0; i < n; ++i) {
auto a = m_ranges[i];
auto b = o.m_ranges[i];
if (a.first != b.first) return a.first < b.first;
if (a.second != b.second) return a.second < b.second;
}
return m_ranges.size() < o.m_ranges.size();
}
unsigned range_predicate::hash() const {
// FNV-1a 32-bit over (max_char, then each (lo, hi)).
uint32_t h = 2166136261u;
auto step = [&](uint32_t x) {
h ^= x;
h *= 16777619u;
};
step(m_max_char);
for (auto [lo, hi] : m_ranges) {
step(lo);
step(hi);
}
return h;
}
// -----------------------------------------------------------------------
// Boolean operations
// -----------------------------------------------------------------------
namespace {
// Append (lo, hi) to result, merging with the previous range if
// adjacent or overlapping. Maintains canonical form.
inline void append_merged(svector<std::pair<unsigned, unsigned>>& result,
unsigned lo, unsigned hi) {
SASSERT(lo <= hi);
if (!result.empty() && result.back().second + 1 >= lo) {
if (result.back().second < hi)
result.back().second = hi;
} else {
result.push_back({lo, hi});
}
}
}
range_predicate range_predicate::operator|(range_predicate const& o) const {
SASSERT(m_max_char == o.m_max_char);
range_predicate r(m_max_char);
unsigned i = 0, j = 0;
const unsigned n = m_ranges.size();
const unsigned m = o.m_ranges.size();
while (i < n && j < m) {
auto a = m_ranges[i];
auto b = o.m_ranges[j];
if (a.first <= b.first) {
append_merged(r.m_ranges, a.first, a.second);
++i;
} else {
append_merged(r.m_ranges, b.first, b.second);
++j;
}
}
while (i < n) {
auto a = m_ranges[i++];
append_merged(r.m_ranges, a.first, a.second);
}
while (j < m) {
auto b = o.m_ranges[j++];
append_merged(r.m_ranges, b.first, b.second);
}
SASSERT(r.well_formed());
return r;
}
range_predicate range_predicate::operator&(range_predicate const& o) const {
SASSERT(m_max_char == o.m_max_char);
range_predicate r(m_max_char);
unsigned i = 0, j = 0;
const unsigned n = m_ranges.size();
const unsigned m = o.m_ranges.size();
while (i < n && j < m) {
auto [a_lo, a_hi] = m_ranges[i];
auto [b_lo, b_hi] = o.m_ranges[j];
unsigned lo = std::max(a_lo, b_lo);
unsigned hi = std::min(a_hi, b_hi);
if (lo <= hi)
r.m_ranges.push_back({lo, hi});
// Advance the range that ends first.
if (a_hi < b_hi) ++i;
else if (b_hi < a_hi) ++j;
else { ++i; ++j; }
}
SASSERT(r.well_formed());
return r;
}
range_predicate range_predicate::operator~() const {
range_predicate r(m_max_char);
unsigned cursor = 0;
for (auto [lo, hi] : m_ranges) {
if (cursor < lo)
r.m_ranges.push_back({cursor, lo - 1});
// Step past hi without overflow: hi <= m_max_char and we
// only step when more characters remain.
if (hi >= m_max_char) {
cursor = m_max_char + 1; // sentinel: no more characters
break;
}
cursor = hi + 1;
}
if (cursor <= m_max_char)
r.m_ranges.push_back({cursor, m_max_char});
SASSERT(r.well_formed());
return r;
}
range_predicate range_predicate::operator-(range_predicate const& o) const {
SASSERT(m_max_char == o.m_max_char);
// A - B by linear sweep: for each range of A, subtract overlapping
// ranges of B. Both inputs are sorted so we advance j monotonically.
range_predicate r(m_max_char);
unsigned j = 0;
const unsigned m = o.m_ranges.size();
for (auto [a_lo, a_hi] : m_ranges) {
unsigned cursor = a_lo;
while (j < m && o.m_ranges[j].second < cursor)
++j;
unsigned k = j;
while (k < m && o.m_ranges[k].first <= a_hi) {
auto [b_lo, b_hi] = o.m_ranges[k];
if (cursor < b_lo)
r.m_ranges.push_back({cursor, std::min(a_hi, b_lo - 1)});
if (b_hi >= a_hi) {
cursor = a_hi + 1;
break;
}
cursor = b_hi + 1;
++k;
}
if (cursor <= a_hi)
r.m_ranges.push_back({cursor, a_hi});
}
SASSERT(r.well_formed());
return r;
}
range_predicate range_predicate::operator^(range_predicate const& o) const {
SASSERT(m_max_char == o.m_max_char);
// (A | B) - (A & B), but implemented directly with one linear sweep
// over the union of breakpoints.
return (*this | o) - (*this & o);
}
// -----------------------------------------------------------------------
// Display
// -----------------------------------------------------------------------
std::ostream& range_predicate::display(std::ostream& out) const {
if (m_ranges.empty()) {
return out << "[]";
}
out << "[";
bool first = true;
for (auto [lo, hi] : m_ranges) {
if (!first) out << ",";
first = false;
if (lo == hi)
out << lo;
else
out << lo << "-" << hi;
}
return out << "]";
}
}

View file

@ -0,0 +1,127 @@
/*++
Copyright (c) 2026 Microsoft Corporation
Module Name:
seq_range_predicate.h
Abstract:
Specialized range-algebra over an unsigned character domain [0, max_char].
A range_predicate represents a subset of the character domain as a
sorted sequence of non-overlapping, non-adjacent, non-empty ranges:
[(lo_0, hi_0), (lo_1, hi_1), ...] with hi_i + 1 < lo_{i+1}.
The representation is canonical, so two range_predicates over the same
domain are extensionally equivalent iff their internal vectors are
elementwise equal.
All Boolean operations (union, intersection, complement, difference)
are linear in the total number of ranges and produce the canonical
representation.
Intended use:
* path conditions for symbolic derivative computation,
* OneStep predicates capturing length-1 acceptance,
* smart-constructor side conditions for regex rewrites such as
R & psi --> toregex(OneStep(R) & psi).
The type is a pure value: no ast_manager allocation occurs in its
construction or its Boolean operations. Conversion to and from
expr* is the responsibility of a separate translator (see callers
in seq_derive / seq_rewriter).
Authors:
Margus Veanes (veanes) 2026
--*/
#pragma once
#include "util/vector.h"
#include <iosfwd>
#include <utility>
namespace seq {
class range_predicate {
using range_t = std::pair<unsigned, unsigned>;
using ranges_t = svector<range_t>;
// Sorted by first; ranges are disjoint and non-adjacent;
// every range satisfies lo <= hi <= m_max_char.
ranges_t m_ranges;
unsigned m_max_char { 0 };
// Invariant check used in debug builds.
bool well_formed() const;
public:
range_predicate() = default;
explicit range_predicate(unsigned max_char) : m_max_char(max_char) {}
// ---------------- Factory functions ----------------
static range_predicate empty(unsigned max_char);
static range_predicate top(unsigned max_char);
static range_predicate singleton(unsigned c, unsigned max_char);
static range_predicate range(unsigned lo, unsigned hi, unsigned max_char);
// ---------------- Observers ----------------
unsigned max_char() const { return m_max_char; }
unsigned num_ranges() const { return m_ranges.size(); }
range_t operator[](unsigned i) const { return m_ranges[i]; }
ranges_t const& ranges() const { return m_ranges; }
bool is_empty() const { return m_ranges.empty(); }
bool is_top() const {
return m_ranges.size() == 1
&& m_ranges[0].first == 0
&& m_ranges[0].second == m_max_char;
}
bool is_singleton(unsigned& c) const {
if (m_ranges.size() != 1) return false;
if (m_ranges[0].first != m_ranges[0].second) return false;
c = m_ranges[0].first;
return true;
}
bool contains(unsigned c) const;
// Number of characters in the predicate (well-defined for any domain).
uint64_t cardinality() const;
// ---------------- Equality, ordering, hashing ----------------
bool equals(range_predicate const& o) const;
bool operator==(range_predicate const& o) const { return equals(o); }
bool operator!=(range_predicate const& o) const { return !equals(o); }
// Total order: lexicographic on the canonical range sequence,
// with shorter sequences ordered before longer prefixes.
// Predicates over different domains compare by max_char first.
bool operator<(range_predicate const& o) const;
bool less_than(range_predicate const& o) const { return *this < o; }
unsigned hash() const;
// ---------------- Boolean operations ----------------
range_predicate operator|(range_predicate const& o) const; // union
range_predicate operator&(range_predicate const& o) const; // intersection
range_predicate operator-(range_predicate const& o) const; // difference
range_predicate operator^(range_predicate const& o) const; // symmetric diff
range_predicate operator~() const; // complement
// ---------------- Display ----------------
std::ostream& display(std::ostream& out) const;
};
inline std::ostream& operator<<(std::ostream& out, range_predicate const& p) {
return p.display(out);
}
}

View file

@ -85,41 +85,6 @@ namespace seq {
return is_ground(r);
}
/*
Collect the leaves of a t-regex der (an ITE / antimirov union /
union-tree with regex leaves) into the output vector. Empty
(re.empty) leaves are dropped.
Returns false if we encountered an unexpected node (e.g. a free
variable creeping in) in that case the caller should bail out.
*/
bool regex_bisim::collect_leaves(expr* der, expr_ref_vector& leaves) {
ptr_vector<expr> work;
obj_hashtable<expr> seen;
work.push_back(der);
seen.insert(der);
while (!work.empty()) {
expr* e = work.back();
work.pop_back();
expr* c = nullptr, * t = nullptr, * f = nullptr;
if (m.is_ite(e, c, t, f) ||
m_util.re.is_union(e, t, f) ||
m_util.re.is_antimirov_union(e, t, f)) {
if (seen.insert_if_not_there(t))
work.push_back(t);
if (seen.insert_if_not_there(f))
work.push_back(f);
continue;
}
if (m_util.re.is_empty(e))
continue;
if (!m_util.is_re(e))
return false;
leaves.push_back(e);
}
return true;
}
/*
Fast inequivalence check based on the get_info().classical flag.
@ -228,15 +193,19 @@ namespace seq {
m_worklist.pop_back();
// Compute the symbolic derivative wrt the canonical variable
// (:var 0). The result is a transition regex (ITE tree) whose
// leaves are regex expressions. We use the classical Brzozowski
// entry point so the derivative stays as a single TRegex and
// does not lift unions to the top via antimirov nodes — this
// preserves the XOR-pair invariant the bisimulation relies on.
expr_ref d(m_rw.mk_brz_derivative(r), m);
// (:var 0) and enumerate its reachable leaves in fully
// ITE-hoisted normal form. Every if-then-else over the input
// character — even one that would otherwise be buried under a
// concat or union — is hoisted to the top and infeasible
// minterms are pruned, so each leaf is a ground regex free of
// (:var 0) whose nullability is always decidable. Unions are
// kept intact as single leaves (a union leaf denotes a single
// bisimulation state, never a split into separate states).
expr_ref_pair_vector cofs(m);
m_rw.brz_derivative_cofactors(r, cofs);
expr_ref_vector leaves(m);
if (!collect_leaves(d, leaves))
return l_undef;
for (auto const& p : cofs)
leaves.push_back(p.second);
// First pass: check for any nullable leaf (definitive
// distinguishing empty-continuation word) or any classically

View file

@ -74,7 +74,6 @@ namespace seq {
unsigned node_of(expr* r);
bool merge_leaf(expr* xor_pair);
bool collect_leaves(expr* der, expr_ref_vector& leaves);
lbool nullability(expr* r);
bool is_supported(expr* r);
// Returns true if the leaf l proves that the original pair is

File diff suppressed because it is too large Load diff

View file

@ -18,7 +18,9 @@ Notes:
--*/
#pragma once
#include "seq_split.h"
#include "ast/seq_decl_plugin.h"
#include "ast/rewriter/seq_derive.h"
#include "ast/ast_pp.h"
#include "ast/arith_decl_plugin.h"
#include "ast/rewriter/rewriter_types.h"
@ -129,16 +131,21 @@ class seq_rewriter {
void insert(decl_kind op, expr* a, expr* b, expr* c, expr* r);
};
friend class seq::derive;
seq_util m_util;
seq_subset m_subset;
seq_split m_split;
arith_util m_autil;
bool_rewriter m_br;
seq::derive m_derive;
// re2automaton m_re2aut;
op_cache m_op_cache;
expr_ref_vector m_es, m_lhs, m_rhs;
bool m_coalesce_chars;
bool m_in_bisim { false };
bool m_coalesce_chars = true;
bool m_in_bisim { false };
unsigned m_re_deriv_depth { 0 };
static const unsigned m_max_re_deriv_depth = 512;
enum length_comparison {
shorter_c,
@ -175,50 +182,22 @@ class seq_rewriter {
//replace b in a by c into result
void replace_all_subvectors(expr_ref_vector const& as, expr_ref_vector const& bs, expr* c, expr_ref_vector& result);
// Calculate derivative, memoized and enforcing a normal form
expr_ref is_nullable_rec(expr* r);
expr_ref mk_derivative_rec(expr* ele, expr* r);
expr_ref mk_der_op(decl_kind k, expr* a, expr* b);
expr_ref mk_der_op_rec(decl_kind k, expr* a, expr* b);
expr_ref mk_der_concat(expr* a, expr* b);
expr_ref mk_der_union(expr* a, expr* b);
expr_ref mk_der_inter(expr* a, expr* b);
expr_ref mk_der_xor(expr* a, expr* b);
expr_ref mk_der_compl(expr* a);
expr_ref mk_der_cond(expr* cond, expr* ele, sort* seq_sort);
expr_ref mk_der_antimirov_union(expr* r1, expr* r2);
bool ite_bdds_compatible(expr* a, expr* b);
/* if r has the form deriv(en..deriv(e1,to_re(s))..) returns 's = [e1..en]' else returns '() in r'*/
expr_ref is_nullable_symbolic_regex(expr* r, sort* seq_sort);
#ifdef Z3DEBUG
bool check_deriv_normal_form(expr* r, int level = 3);
#endif
// For replace_all(x, a, b) in R: transform R so that
// - occurrences of b_ch are replaced by union(to_re(a_str), to_re(b_str))
// - occurrences of a_ch are replaced by empty (replace_all never outputs a)
expr_ref re_replace_char(expr *r, unsigned a_ch, unsigned b_ch, expr *a_str, expr *b_str);
void mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result);
expr_ref mk_antimirov_deriv(expr* e, expr* r, expr* path);
expr_ref mk_in_antimirov_rec(expr* s, expr* d);
expr_ref mk_in_antimirov(expr* s, expr* d);
expr_ref mk_antimirov_deriv_intersection(expr* elem, expr* d1, expr* d2, expr* path);
expr_ref mk_antimirov_deriv_concat(expr* d, expr* r);
expr_ref mk_antimirov_deriv_negate(expr* elem, expr* d);
expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2);
expr_ref mk_antimirov_deriv_restrict(expr* elem, expr* d1, expr* cond);
expr_ref mk_regex_reverse(expr* r);
expr_ref mk_regex_concat(expr* r1, expr* r2);
expr_ref merge_regex_sets(expr* r1, expr* r2, expr* unit, std::function<bool(expr*, expr*&, expr*&)>& decompose, std::function<expr* (expr*, expr*)>& compose);
// elem is (:var 0) and path a condition that may have (:var 0) as a free variable
// simplify path, e.g., (:var 0) = 'a' & (:var 0) = 'b' is simplified to false
expr_ref simplify_path(expr* elem, expr* path);
// expr_ref simplify_path(expr* elem, expr* path);
bool lt_char(expr* ch1, expr* ch2);
bool eq_char(expr* ch1, expr* ch2);
bool neq_char(expr* ch1, expr* ch2);
bool le_char(expr* ch1, expr* ch2);
bool pred_implies(expr* a, expr* b);
bool are_complements(expr* r1, expr* r2) const;
bool is_subset(expr* r1, expr* r2) const;
@ -264,6 +243,14 @@ class seq_rewriter {
br_status mk_re_union0(expr* a, expr* b, expr_ref& result);
br_status mk_re_inter0(expr* a, expr* b, expr_ref& result);
br_status mk_re_complement(expr* a, expr_ref& result);
// Range-set collapse helpers: if the operands form a boolean
// combination of character-class regexes, materialize the result as a
// canonical regex over a single range_predicate. See
// ast/rewriter/seq_range_collapse.h for the recognized fragment.
// NOTE: re.complement is intentionally not in this set because it
// operates at the sequence level, not the character-class level.
bool try_collapse_re_union(expr* a, expr* b, expr_ref& result);
bool try_collapse_re_inter(expr* a, expr* b, expr_ref& result);
br_status mk_re_star(expr* a, expr_ref& result);
br_status mk_re_diff(expr* a, expr* b, expr_ref& result);
br_status mk_re_xor(expr* a, expr* b, expr_ref& result);
@ -347,10 +334,10 @@ class seq_rewriter {
lbool some_string_in_re(expr_mark& visited, expr* r, unsigned_vector& str);
public:
seq_rewriter(ast_manager & m, params_ref const & p = params_ref()):
m_util(m), m_subset(m_util.re), m_split(*this), m_autil(m), m_br(m, p), // m_re2aut(m),
seq_rewriter(ast_manager & m, params_ref const & p = params_ref()) :
m_util(m), m_subset(m_util.re), m_split(*this), m_autil(m), m_br(m, p), m_derive(m, *this), // m_re2aut(m),
m_op_cache(m), m_es(m),
m_lhs(m), m_rhs(m), m_coalesce_chars(true) {
m_lhs(m), m_rhs(m) {
}
ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); }
@ -361,7 +348,7 @@ public:
static void get_param_descrs(param_descrs & r);
bool coalesce_chars() const { return m_coalesce_chars; }
// bool coalesce_chars() const { return m_coalesce_chars; }
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
@ -377,6 +364,34 @@ public:
return result;
}
expr_ref mk_xor0(expr *a, expr *b) {
expr_ref result(m());
if (mk_re_xor0(a, b, result) == BR_FAILED)
result = re().mk_xor(a, b);
return result;
}
expr_ref mk_union(expr *a, expr *b) {
expr_ref result(m());
if (mk_re_union(a, b, result) == BR_FAILED)
result = re().mk_union(a, b);
return result;
}
expr_ref mk_inter(expr *a, expr *b) {
expr_ref result(m());
if (mk_re_inter(a, b, result) == BR_FAILED)
result = re().mk_inter(a, b);
return result;
}
expr_ref mk_complement(expr *a) {
expr_ref result(m());
if (mk_re_complement(a, result) == BR_FAILED)
result = re().mk_complement(a);
return result;
}
/*
* makes concat and simplifies
*/
@ -460,11 +475,35 @@ public:
variable v0 = (:var 0). Unlike `mk_derivative` this entry point keeps
the symbolic derivative as a single transition regex (TRegex): boolean
operators are pushed into the ITE leaves rather than lifted to the top
via _OP_RE_ANTIMIROV_UNION. Used by the regex_bisim equivalence
as a union. Used by the regex_bisim equivalence
procedure which relies on each leaf of D(p XOR q) being a coherent
XOR pair (D_v p) XOR (D_v q).
*/
expr_ref mk_brz_derivative(expr* r);
expr_ref mk_brz_derivative(expr *r) {
return mk_derivative(r);
}
/*
Enumerate the cofactors (min-terms) of a transition regex r taken with
respect to ele. Produces (path_condition, leaf_regex) pairs for every
feasible path through the ITE-tree, pruning infeasible character ranges.
Delegates to the derivative engine so the same path/interval context used
while hoisting ITEs is reused for the leaf simplification.
*/
void get_cofactors(expr* ele, expr* r, expr_ref_pair_vector& result) {
m_derive.get_cofactors(ele, r, result);
}
/*
Compute the symbolic derivative of r and enumerate its reachable leaves
in fully ITE-hoisted normal form: a list of (path_condition, target)
pairs where every target is free of (:var 0) (so nullability is always
decidable) and unions are kept intact as single states. Used by
regex_bisim, which consumes the targets and ignores the path conditions.
*/
void brz_derivative_cofactors(expr* r, expr_ref_pair_vector& result) {
m_derive.derivative_cofactors(r, result);
}
// heuristic elimination of element from condition that comes form a derivative.
// special case optimization for conjunctions of equalities, disequalities and ranges.
@ -475,15 +514,7 @@ public:
/* Apply simplifications to the intersection to keep it normalized (r1 and r2 are not normalized)*/
expr_ref mk_regex_inter_normalize(expr* r1, expr* r2);
/*
* Extract some sequence that is a member of r.
* result is set to a concrete sequence expression if l_true is returned.
* For string-typed regexes, delegates to some_string_in_re.
* For other sequence types, checks nullability and returns the empty
* sequence if the regex accepts it; otherwise returns l_undef.
* Returns l_false if the regex is known to be empty.
*/
lbool some_seq_in_re(expr* r, expr_ref& result);
expr_ref mk_regex_concat(expr *r1, expr *r2);
/*
* Extract some string that is a member of r.

View file

@ -1,3 +1,4 @@
/*++
Copyright (c) 2026 Microsoft Corporation

View file

@ -19,7 +19,7 @@ Author:
bool seq_subset::is_subset_rec(expr* a, expr* b, unsigned depth) const {
while (true) {
if (a == b)
return true;
if (m_re.is_empty(a))
@ -30,7 +30,7 @@ bool seq_subset::is_subset_rec(expr* a, expr* b, unsigned depth) const {
return true;
if (depth >= m_max_depth)
return false;
return false;
expr* a1 = nullptr, * a2 = nullptr, * b1 = nullptr, * b2 = nullptr;
unsigned la, ua, lb, ub;
@ -39,16 +39,12 @@ bool seq_subset::is_subset_rec(expr* a, expr* b, unsigned depth) const {
if (m_re.is_dot_plus(b) && m_re.get_info(a).nullable == l_false)
return true;
// a ⊆ a*
if (m_re.is_star(b, b1) && is_subset_rec(a, b1, depth))
return true;
// e ⊆ a*
if (m_re.is_epsilon(a) && m_re.is_star(b, b1))
return true;
// R ⊆ R*
if (m_re.is_star(b, b1) && is_subset_rec(a, b1, depth + 1))
// a ⊆ a*: if b = b1* and a ⊆ b1, then a ⊆ b1*
if (m_re.is_star(b, b1) && is_subset_rec(a, b1, depth))
return true;
// R1* ⊆ R2* if R1 ⊆ R2
@ -112,6 +108,12 @@ bool seq_subset::is_subset_rec(expr* a, expr* b, unsigned depth) const {
if (m_re.is_concat(b, b1, b2) && m_re.is_full_seq(b1) && is_subset_rec(a, b2, depth))
return true;
// prefix absorption: P·R' ⊆ Σ*·R' for any prefix P (since P ⊆ Σ*).
// Detect that a has R' (= b2) as a concatenation suffix, where b = Σ*·R'.
// Covers contains-patterns, e.g. Σ*·a·Σ*·b·Σ* ⊆ Σ*·b·Σ*.
if (m_re.is_concat(b, b1, b2) && m_re.is_full_seq(b1) && ends_with(a, b2))
return true;
// R ⊆ R'·Σ* if R ⊆ R'
if (m_re.is_concat(b, b1, b2) && m_re.is_full_seq(b2) && is_subset_rec(a, b1, depth))
return true;
@ -144,3 +146,30 @@ bool seq_subset::is_subset_rec(expr* a, expr* b, unsigned depth) const {
bool seq_subset::is_subset(expr* a, expr* b) const {
return is_subset_rec(a, b, 0);
}
bool seq_subset::ends_with(expr* a, expr* suf) const {
if (a == suf)
return true;
// Flatten both regexes into their sequence of concatenation factors
// (independent of left/right associativity) and test list-suffix equality.
ptr_vector<expr> af, sf;
flatten_concat(a, af);
flatten_concat(suf, sf);
if (sf.size() > af.size())
return false;
unsigned off = af.size() - sf.size();
for (unsigned i = 0; i < sf.size(); ++i)
if (af[off + i] != sf[i])
return false;
return true;
}
void seq_subset::flatten_concat(expr* a, ptr_vector<expr>& out) const {
expr* a1 = nullptr, * a2 = nullptr;
if (m_re.is_concat(a, a1, a2)) {
flatten_concat(a1, out);
flatten_concat(a2, out);
}
else
out.push_back(a);
}

View file

@ -24,6 +24,12 @@ class seq_subset {
bool is_subset_rec(expr* a, expr* b, unsigned depth) const;
// true if regex a, viewed as a flattened concatenation, has suf as a
// structural (concatenation) suffix.
bool ends_with(expr* a, expr* suf) const;
void flatten_concat(expr* a, ptr_vector<expr>& out) const;
public:
explicit seq_subset(seq_util::rex& re) : m_re(re) {}
bool is_subset(expr* a, expr* b) const;

View file

@ -0,0 +1,681 @@
/**
* term_enumeration.cpp - Bottom-up term enumeration module for Z3
*
* Inspired by the Probe synthesizer (Barke et al., "Just-in-Time Learning
* for Bottom-Up Enumerative Synthesis"). Adapted to use Z3's internal APIs.
*
* Key ideas:
* - Terms are enumerated bottom-up by "cost" (calculated by tree size).
* - A grammar describes which function symbols (operators) and leaves
* (constants, variables) are available for enumeration.
*/
#include <sstream>
#include <functional>
#include <string>
#include "util/vector.h"
#include "util/scoped_ptr_vector.h"
#include "util/obj_hashtable.h"
#include "util/uint_set.h"
#include "ast/ast.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_pp.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/rewriter/term_enumeration.h"
namespace term_enum {
// ============================================================================
// grammar production rule
// ============================================================================
/**
* A production describes how to construct a term from child terms.
* - domain: the sort required for each child
* - range: the sort of the produced term
* - builder: given a vector of child exprs, produce the result expr
*/
struct production {
std::string name;
sort_ref range;
sort_ref_vector domain;
std::function<expr_ref(expr_ref_vector const&)> builder;
bool is_leaf() const { return domain.empty(); }
};
// ============================================================================
// grammar
// ============================================================================
/**
* A grammar groups productions into leaves (arity 0) and operators (arity > 0).
*/
class grammar {
public:
grammar(ast_manager& m) : m(m), m_pinned(m) {}
void add_production(production* p) {
if (p->is_leaf())
m_leaves.push_back(p);
else
m_operators.push_back(p);
}
scoped_ptr_vector<production> const& leaves() const { return m_leaves; }
scoped_ptr_vector<production> const& operators() const { return m_operators; }
ast_manager& mgr() const { return m; }
void add_func_decl(func_decl *f) {
if (m_seen.contains(f))
return;
m_pinned.push_back(f);
m_seen.insert(f);
sort_ref range(f->get_range(), m);
sort_ref_vector dom(m);
for (unsigned i = 0; i < f->get_arity(); ++i)
dom.push_back(sort_ref(f->get_domain(i), m));
add_production(alloc(production, {f->get_name().str(), range, dom, [this, f](expr_ref_vector const &args) {
return expr_ref(m.mk_app(f, args), m);
}}));
}
void add_expr(expr *e) {
if (m_seen.contains(e))
return;
m_pinned.push_back(e);
m_seen.insert(e);
sort_ref range(e->get_sort(), m);
sort_ref_vector dom(m);
std::stringstream ss;
ss << mk_bounded_pp(e, m);
std::string name = ss.str();
add_production(alloc(production, {name, range, dom, [this, e](expr_ref_vector const&) { return expr_ref(e, m); }}));
}
std::ostream& display(std::ostream& out) const {
out << "Leaves:\n";
for (auto const *p : m_leaves) {
out << " " << p->name << " : " << mk_pp(p->range, m) << "\n";
}
out << "Operators:\n";
for (auto const *p : m_operators) {
out << " " << p->name << " : (";
for (unsigned i = 0; i < p->domain.size(); ++i) {
if (i > 0)
out << ", ";
out << mk_pp(p->domain[i], m);
}
out << ") -> " << mk_pp(p->range, m) << "\n";
}
return out;
}
private:
ast_manager& m;
ast_ref_vector m_pinned;
scoped_ptr_vector<production> m_leaves;
scoped_ptr_vector<production> m_operators;
obj_hashtable<ast> m_seen;
};
// ============================================================================
// Term Bank - stores enumerated terms by cost and sort
// ============================================================================
using cost_terms = vector<std::pair<expr*, unsigned>>;
class term_bank {
using sort_term_map = obj_map<sort, ptr_vector<expr>>;
public:
term_bank(ast_manager& m) : m(m), m_pinned(m) {}
~term_bank() {
for (auto s : m_terms)
dealloc(s);
}
void reset() {
m_pinned.reset();
m_terms.clear();
}
void add(expr* term, unsigned cost) {
sort* s = term->get_sort();
m_pinned.push_back(term);
if (cost >= m_terms.size())
m_terms.resize(cost + 1);
if (!m_terms[cost])
m_terms[cost] = alloc(sort_term_map);
m_terms[cost]->insert_if_not_there(s, ptr_vector<expr>()).push_back(term);
}
/** Get all terms of a given sort up to (and including) max_cost */
cost_terms get_by_sort(sort* s, unsigned max_cost) const {
cost_terms result;
for (unsigned c = 0; c <= max_cost; ++c) {
if (c >= m_terms.size())
break;
if (!m_terms[c]->contains(s))
continue;
for (auto t : m_terms[c]->find(s))
result.push_back({t, c});
}
return result;
}
// Return true if there is at least one term at/above `cost` whose sort is
// not in `sorts` (i.e., enumeration can still produce a new requested sort).
bool is_productive(unsigned cost, uint_set const& sorts) {
for (unsigned i = cost; i < m_terms.size(); ++i) {
if (!m_terms[i])
continue;
for (auto const& entry : *m_terms[i]) {
sort* term_sort = entry.m_key;
if (!sorts.contains(term_sort->get_small_id()))
return true;
}
}
return false;
}
ptr_vector<expr> null_ptr_vector;
ptr_vector<expr> const &get_by_cost_and_sort(unsigned cost, sort *s) const {
if (cost >= m_terms.size() || !m_terms[cost] || !m_terms[cost]->contains(s))
return null_ptr_vector;
return m_terms[cost]->find(s);
}
std::ostream& display(std::ostream& out) const {
for (unsigned cost = 0; cost < m_terms.size(); ++cost) {
if (!m_terms[cost])
continue;
out << "cost " << cost << ":\n";
for (auto& [s, terms] : *m_terms[cost]) {
out << " sort " << mk_pp(s, m) << ":\n";
for (expr* e : terms) {
out << " #" << e->get_id() << " ";
if (cost == 0) {
out << mk_bounded_pp(e, m);
}
else if (is_app(e)) {
app* a = to_app(e);
out << a->get_decl()->get_name() << "(";
bool first = true;
for (expr* arg : *a) {
if (!first) out << ", ";
first = false;
out << "#" << arg->get_id();
}
out << ")";
}
out << "\n";
}
}
}
return out;
}
private:
ast_manager& m;
expr_ref_vector m_pinned;
// cost -> sort -> terms
ptr_vector<sort_term_map> m_terms;
};
// ============================================================================
// Children Iterator - generates all combinations of child terms
// ============================================================================
/**
* Iterates over all tuples (c1, c2, ..., cn) where each ci has the required
* sort, drawn from the term bank, with at least one child at the current
* cost - 1 (to avoid regenerating previously seen terms).
*/
class children_iterator {
public:
children_iterator(ast_manager& m, production const& prod, term_bank const& bank, unsigned current_cost)
: m(m), m_done(false)
{
m_arity = prod.domain.size();
if (m_arity == 0) {
m_done = true;
return;
}
for (unsigned i = 0; i < m_arity; ++i) {
m_candidates.push_back(bank.get_by_sort(prod.domain[i], current_cost - 1));
if (m_candidates.back().empty()) {
m_done = true;
return;
}
}
m_indices.resize(m_arity, 0);
}
bool has_next(unsigned cost) {
while (!m_done) {
if (m.limit().is_canceled())
return false;
if (has_child_at_cost(cost))
return true;
advance();
}
return false;
}
expr_ref_vector next(unsigned& cost) {
expr_ref_vector result(m);
cost = 1;
for (unsigned i = 0; i < m_arity; ++i) {
auto [e, c] = m_candidates[i].get(m_indices[i]);
cost += c;
result.push_back(e);
}
advance();
return result;
}
private:
ast_manager& m;
unsigned m_arity;
bool m_done;
vector<cost_terms> m_candidates;
svector<unsigned> m_indices;
bool has_child_at_cost(unsigned cost) const {
for (unsigned i = 0; i < m_arity; ++i) {
auto [e, c] = m_candidates[i].get(m_indices[i]);
if (c + 1 == cost)
return true;
}
return false;
}
void advance() {
for (auto i = m_arity; i-- > 0;) {
m_indices[i]++;
if (m_indices[i] < m_candidates[i].size()) return;
m_indices[i] = 0;
}
m_done = true;
}
};
// ============================================================================
// bottom_up_enumerator - the main bottom-up term enumeration engine
// ============================================================================
class bottom_up_enumerator {
public:
bottom_up_enumerator(grammar& grammar)
: m_grammar(grammar), m(grammar.mgr()),
m_bank(grammar.mgr()), m_pending(grammar.mgr()), m_rewriter(grammar.mgr())
{}
void set_target_sort(sort *s) {
m_target_sort = s;
}
bool has_next() {
if (m_pending) return true;
m_pending = find_next();
return m_pending != nullptr;
}
expr_ref next() {
if (!m_pending)
m_pending = find_next();
expr_ref result(m_pending, m);
m_pending = nullptr;
return result;
}
term_bank const& bank() const { return m_bank; }
std::ostream& display(std::ostream& out) const {
m_grammar.display(out);
return m_bank.display(out);
}
void reset() {
m_cost = 0;
m_leaf_idx = 0;
m_op_idx = 0;
m_state = State::Leaves;
m_bank.reset();
m_pending = nullptr;
m_rewriter.reset();
m_seen_terms.reset();
m_children_iter.reset();
}
expr* add_term(expr_ref const& term, unsigned cost) {
expr_ref simplified(m);
m_rewriter(term, simplified);
if (m_seen_terms.contains(simplified))
return nullptr;
IF_VERBOSE(10, verbose_stream() << "add " << simplified << "\n");
m_seen_terms.insert(simplified);
m_bank.add(simplified, cost);
return simplified;
}
private:
enum class State { Leaves, Operators, Done };
grammar& m_grammar;
ast_manager& m;
term_bank m_bank;
unsigned m_cost = 0;
unsigned m_leaf_idx = 0;
unsigned m_op_idx = 0;
unsigned m_bank_idx = 0;
unsigned m_bank_size = 0;
bool m_made_progress = false;
uint_set m_sorts_produced;
State m_state = State::Leaves;
expr_ref m_pending;
th_rewriter m_rewriter;
obj_hashtable<expr> m_seen_terms;
std::unique_ptr<children_iterator> m_children_iter;
sort *m_target_sort = nullptr;
bool sort_matches(expr* e) const {
return !m_target_sort || e->get_sort() == m_target_sort;
}
expr* find_next() {
while (true) {
if (m.limit().is_canceled()) {
m_state = State::Done;
return nullptr;
}
switch (m_state) {
case State::Leaves:
while (m_leaf_idx < m_grammar.leaves().size()) {
production const &prod = *m_grammar.leaves()[m_leaf_idx];
m_leaf_idx++;
expr_ref_vector empty_args(m);
expr_ref term = prod.builder(empty_args);
expr* r = add_term(term, 0);
if (r && sort_matches(r))
return r;
}
m_state = State::Operators;
m_cost = 1;
m_op_idx = 0;
m_bank_idx = 0;
m_bank_size = get_bank_size();
m_made_progress = false;
m_sorts_produced.reset();
m_children_iter.reset();
break;
case State::Operators: {
expr* result = enumerate_operators();
if (result)
return result;
m_cost++;
m_op_idx = 0;
m_bank_idx = 0;
m_bank_size = get_bank_size();
m_children_iter.reset();
if (!m_made_progress && !m_bank.is_productive(m_cost, m_sorts_produced)) {
m_state = State::Done;
return nullptr;
}
if (m_sorts_produced.contains(m_target_sort->get_small_id()))
m_sorts_produced.reset();
m_made_progress = false;
break;
}
case State::Done:
return nullptr;
}
}
}
unsigned get_bank_size() const {
auto const &terms = m_bank.get_by_cost_and_sort(m_cost, m_target_sort);
return terms.size();
}
expr *enumerate_operators() {
auto const &ops = m_grammar.operators();
while (true) {
if (m.limit().is_canceled())
return nullptr;
// first find terms at m_cost that were already created
if (m_bank_idx < m_bank_size) {
auto const &terms = m_bank.get_by_cost_and_sort(m_cost, m_target_sort);
auto t = terms.get(m_bank_idx);
m_bank_idx++;
SASSERT(sort_matches(t));
return t;
}
// then create new terms using children at cost below current m_cost.
if (m_children_iter && m_children_iter->has_next(m_cost)) {
unsigned new_cost = 0;
expr_ref_vector children = m_children_iter->next(new_cost);
production const &prod = *ops[m_op_idx - 1];
expr_ref term = prod.builder(children);
// IF_VERBOSE(0, verbose_stream() << term << "\n");
SASSERT(new_cost >= m_cost);
expr* r = add_term(term, new_cost);
if (!r)
continue;
unsigned sort_id = r->get_sort()->get_small_id();
if (!m_sorts_produced.contains(sort_id))
m_made_progress = true;
m_sorts_produced.insert(sort_id);
if (sort_matches(r) && new_cost == m_cost) {
return r;
}
continue;
}
if (m_op_idx >= ops.size())
return nullptr;
production const &prod = *ops[m_op_idx];
m_op_idx++;
m_children_iter = std::make_unique<children_iterator>(m, prod, m_bank, m_cost);
}
}
};
} // namespace term_enum
// ============================================================================
// term_enumeration public interface implementation
// ============================================================================
struct term_enumeration::imp {
ast_manager& m;
term_enum::grammar m_grammar;
term_enum::bottom_up_enumerator m_bottom_up_enumerator;
std::function<unsigned(expr*)> m_cost;
imp(ast_manager& m) :
m(m), m_grammar(m), m_bottom_up_enumerator(m_grammar) {}
void add_production(func_decl* f) {
m_grammar.add_func_decl(f);
}
void add_production(expr* e) {
m_grammar.add_expr(e);
}
void set_cost(std::function<unsigned(expr*)> const& cost) {
// TODO
}
std::ostream& display(std::ostream& out) const {
return m_bottom_up_enumerator.display(out);
}
};
// -- iterator implementation --
struct term_enumeration::iterator::iter_imp {
imp& m_imp;
ast_manager & m;
sort* m_sort;
unsigned m_cost = 0;
unsigned m_idx = 0;
vector<expr_ref_vector> m_levels;
expr_ref m_current;
bool m_end;
vector<expr_ref_vector> m_vars;
vector<ptr_vector<sort>> m_decls;
vector<vector<symbol>> m_names;
iter_imp(imp& i, sort* s) : m_imp(i), m(i.m), m_sort(s), m_current(i.m), m_end(false) {
m_imp.m_bottom_up_enumerator.reset();
init_sort();
advance();
}
// Sentinel constructor
iter_imp(imp& i) :
m_imp(i), m(i.m), m_sort(nullptr), m_current(i.m), m_end(true) {
UNREACHABLE();
}
void init_sort() {
array_util autil(m);
sort *range = m_sort;
while (autil.is_array(range)) {
m_vars.push_back(expr_ref_vector(m));
m_decls.push_back(ptr_vector<sort>());
m_names.push_back(vector<symbol>());
for (unsigned i = 0; i < get_array_arity(range); ++i) {
m_decls.back().push_back(get_array_domain(range, i));
m_vars.back().push_back(nullptr);
m_names.back().push_back(symbol());
}
expr_ref_vector args(m);
args.push_back(m.mk_const("a", range));
for (unsigned i = 0; i < m_decls.back().size(); ++i) {
args.push_back(m.mk_var(i, m_decls.back().get(i)));
}
app_ref sel(autil.mk_select(args), m);
m_imp.m_grammar.add_func_decl(sel->get_decl());
range = get_array_range(range);
}
unsigned n = 0;
for (unsigned i = m_decls.size(); i-- > 0;) {
for (unsigned j = m_decls[i].size(); j-- > 0;) {
m_vars[i][j] = m.mk_var(n, m_decls[i][j]);
m_names[i][j] = symbol(n);
m_imp.add_production(m_vars[i].get(j));
n++;
}
}
m_sort = range;
m_imp.m_bottom_up_enumerator.set_target_sort(range);
}
void mk_lambda() {
if (!m_current)
return;
for (unsigned i = m_decls.size(); i-- > 0;)
m_current = m.mk_lambda(m_decls[i].size(), m_decls[i].data(), m_names[i].data(), m_current);
}
void advance() {
if (m_end)
return;
m_current = m_imp.m_bottom_up_enumerator.next();
SASSERT(!m_current || m_current->get_sort() == m_sort);
mk_lambda();
if (!m_current)
m_end = true;
}
};
term_enumeration::iterator::iterator(imp& i, sort* s) {
m_imp = alloc(iter_imp, i, s);
}
term_enumeration::iterator::iterator(std::nullptr_t) {
m_imp = nullptr;
}
term_enumeration::iterator::~iterator() {
dealloc(m_imp);
}
expr* term_enumeration::iterator::operator*() {
return m_imp ? m_imp->m_current.get() : nullptr;
}
term_enumeration::iterator& term_enumeration::iterator::operator++() {
if (m_imp) m_imp->advance();
return *this;
}
term_enumeration::iterator term_enumeration::iterator::operator++(int) {
iterator tmp(*this);
++(*this);
return tmp;
}
bool term_enumeration::iterator::operator==(iterator const& other) const {
if (!m_imp && !other.m_imp) return true;
if (!m_imp) return other.m_imp->m_end;
if (!other.m_imp) return m_imp->m_end;
return m_imp->m_end == other.m_imp->m_end &&
m_imp->m_current == other.m_imp->m_current;
}
// -- terms implementation --
term_enumeration::terms::terms(imp* i, sort* s) : m_imp(i), m_sort(s) {}
term_enumeration::iterator term_enumeration::terms::begin() {
return iterator(*m_imp, m_sort);
}
term_enumeration::iterator term_enumeration::terms::end() {
return iterator(nullptr);
}
// -- term_enumeration implementation --
term_enumeration::term_enumeration(ast_manager& m) {
m_imp = alloc(imp, m);
}
term_enumeration::~term_enumeration() {
dealloc(m_imp);
}
void term_enumeration::add_production(func_decl* f) {
m_imp->add_production(f);
}
void term_enumeration::add_production(expr* e) {
m_imp->add_production(e);
}
void term_enumeration::set_cost(std::function<unsigned(expr*)> const& cost) {
m_imp->set_cost(cost);
}
term_enumeration::terms term_enumeration::enum_terms(sort* s) {
return terms(m_imp, s);
}
std::ostream& term_enumeration::display(std::ostream& out) const {
return m_imp->display(out);
}

View file

@ -0,0 +1,50 @@
#pragma once
#include "ast/ast.h"
#include <functional>
class term_enumeration {
struct imp;
imp* m_imp;
public:
term_enumeration(ast_manager& m);
~term_enumeration();
void add_production(func_decl* f);
void add_production(expr* e);
// void add_production(sort *s, std::function<expr *()> g);
// cost function associated with expressions.
// terms are enumerated with increasing cost.
void set_cost(std::function<unsigned(expr*)> const& cost);
class iterator {
struct iter_imp;
iter_imp* m_imp;
public:
iterator(imp& i, sort* s);
iterator(std::nullptr_t);
~iterator();
expr* operator*();
iterator operator++(int);
iterator& operator++();
bool operator!=(iterator const& other) const {
return !(*this == other);
}
bool operator==(iterator const &other) const;
};
class terms {
imp* m_imp;
sort* m_sort;
public:
terms(imp* i, sort* s);
iterator begin();
iterator end();
};
terms enum_terms(sort* s);
std::ostream& display(std::ostream& out) const;
};

View file

@ -241,7 +241,6 @@ void seq_decl_plugin::init() {
m_sigs[OP_RE_OF_PRED] = alloc(psig, m, "re.of.pred", 1, 1, &predA, reA);
m_sigs[OP_RE_REVERSE] = alloc(psig, m, "re.reverse", 1, 1, &reA, reA);
m_sigs[OP_RE_DERIVATIVE] = alloc(psig, m, "re.derivative", 1, 2, AreA, reA);
m_sigs[_OP_RE_ANTIMIROV_UNION] = alloc(psig, m, "re.union", 1, 2, reAreA, reA);
m_sigs[OP_SEQ_TO_RE] = alloc(psig, m, "seq.to.re", 1, 1, &seqA, reA);
m_sigs[OP_SEQ_IN_RE] = alloc(psig, m, "seq.in.re", 1, 2, seqAreA, boolT);
m_sigs[OP_SEQ_REPLACE_RE_ALL] = alloc(psig, m, "str.replace_re_all", 1, 3, seqAreAseqA, seqA);
@ -413,7 +412,6 @@ func_decl* seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
case OP_RE_COMPLEMENT:
case OP_RE_REVERSE:
case OP_RE_DERIVATIVE:
case _OP_RE_ANTIMIROV_UNION:
m_has_re = true;
Z3_fallthrough;
case OP_SEQ_UNIT:
@ -423,7 +421,7 @@ func_decl* seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
case OP_STRING_LE:
case OP_STRING_IS_DIGIT:
case OP_STRING_TO_CODE:
case OP_STRING_FROM_CODE:
case OP_STRING_FROM_CODE:
match(*m_sigs[k], arity, domain, range, rng);
return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k));
@ -1213,6 +1211,17 @@ app* seq_util::rex::mk_of_pred(expr* p) {
return m.mk_app(m_fid, OP_RE_OF_PRED, 0, nullptr, 1, &p);
}
app* seq_util::rex::mk_range(sort* re_sort, unsigned lo, unsigned hi) {
if (lo > hi)
return mk_empty(re_sort);
if (lo == 0 && hi == u.max_char())
return mk_full_char(re_sort);
app* lo_str = u.str.mk_string(zstring(lo));
if (lo == hi)
return mk_to_re(lo_str);
return mk_range(lo_str, u.str.mk_string(zstring(hi)));
}
bool seq_util::rex::is_loop(expr const* n, expr*& body, unsigned& lo, unsigned& hi) const {
if (is_loop(n)) {
app const* a = to_app(n);
@ -1441,7 +1450,7 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const {
print(out, r1);
print(out, r2);
}
else if (re.is_antimirov_union(e, r1, r2) || re.is_union(e, r1, r2)) {
else if (re.is_union(e, r1, r2)) {
out << "(";
print(out, r1);
out << (html_encode ? "&#x22C3;" : "|");
@ -1674,11 +1683,19 @@ seq_util::rex::info seq_util::rex::mk_info_rec(app* e) const {
case OP_RE_OPTION:
i1 = get_info_rec(e->get_arg(0));
return i1.opt();
case OP_RE_RANGE:
case OP_RE_RANGE: {
// A concrete range [lo, hi] with lo <= hi is non-empty and classical.
zstring slo, shi;
if (u.str.is_string(e->get_arg(0), slo) && slo.length() == 1 &&
u.str.is_string(e->get_arg(1), shi) && shi.length() == 1 &&
slo[0] <= shi[0])
return info(true, l_false, 1, true);
// Symbolic or unknown: not classical
return info(true, l_false, 1, false);
}
case OP_RE_FULL_CHAR_SET:
case OP_RE_OF_PRED:
//TBD: check if the character predicate contains uninterpreted symbols or is nonground or is unsat
//TBD: check if the range is unsat
return info(true, l_false, 1, false);
case OP_RE_CONCAT:
i1 = get_info_rec(e->get_arg(0));

View file

@ -109,7 +109,6 @@ enum seq_op_kind {
_OP_REGEXP_EMPTY,
_OP_REGEXP_FULL_CHAR,
_OP_RE_IS_NULLABLE,
_OP_RE_ANTIMIROV_UNION, // Lifted union for antimirov-style derivatives
_OP_SEQ_SKOLEM,
LAST_SEQ_OP
};
@ -525,6 +524,8 @@ public:
app* mk_to_re(expr* s) { return m.mk_app(m_fid, OP_SEQ_TO_RE, 1, &s); }
app* mk_in_re(expr* s, expr* r) { return m.mk_app(m_fid, OP_SEQ_IN_RE, s, r); }
app* mk_range(expr* s1, expr* s2) { return m.mk_app(m_fid, OP_RE_RANGE, s1, s2); }
// Smart constructor: returns re.empty / str.to_re / re.range based on lo vs hi.
app* mk_range(sort* re_sort, unsigned lo, unsigned hi);
app* mk_concat(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_CONCAT, r1, r2); }
app* mk_union(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_UNION, r1, r2); }
app* mk_inter(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_INTERSECT, r1, r2); }
@ -546,7 +547,6 @@ public:
app* mk_of_pred(expr* p);
app* mk_reverse(expr* r) { return m.mk_app(m_fid, OP_RE_REVERSE, r); }
app* mk_derivative(expr* ele, expr* r) { return m.mk_app(m_fid, OP_RE_DERIVATIVE, ele, r); }
app* mk_antimirov_union(expr* r1, expr* r2) { return m.mk_app(m_fid, _OP_RE_ANTIMIROV_UNION, r1, r2); }
bool is_to_re(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_TO_RE); }
bool is_concat(expr const* n) const { return is_app_of(n, m_fid, OP_RE_CONCAT); }
@ -580,7 +580,6 @@ public:
bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); }
bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); }
bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); }
bool is_antimirov_union(expr const* n) const { return is_app_of(n, m_fid, _OP_RE_ANTIMIROV_UNION); }
MATCH_UNARY(is_to_re);
MATCH_BINARY(is_concat);
MATCH_BINARY(is_union);
@ -595,7 +594,6 @@ public:
MATCH_UNARY(is_of_pred);
MATCH_UNARY(is_reverse);
MATCH_BINARY(is_derivative);
MATCH_BINARY(is_antimirov_union);
bool is_loop(expr const* n, expr*& body, unsigned& lo, unsigned& hi) const;
bool is_loop(expr const* n, expr*& body, unsigned& lo) const;
bool is_loop(expr const* n, expr*& body, expr*& lo, expr*& hi) const;

View file

@ -45,7 +45,7 @@ Author:
class dependent_expr_state {
unsigned m_qhead = 0;
bool m_suffix_frozen = false;
unsigned m_num_recfun = 0, m_num_lambdas = 0;
unsigned m_num_recfun = 0;
lbool m_has_quantifiers = l_undef;
ast_mark m_frozen;
func_decl_ref_vector m_frozen_trail;

View file

@ -16,7 +16,7 @@ Abstract:
All variables x, y, z, .. can eventually be eliminated, but the tactic requires a global
analysis between each elimination. We address this by using reference counts and maintaining
a heap of reference counts.
- it does not accomodate side constraints. The more general invertibility reduction methods, such
- it does not accommodate side constraints. The more general invertibility reduction methods, such
as those introduced for bit-vectors use side constraints.
- it is not modular: we detach the expression invertion routines to self-contained code.

View file

@ -20,7 +20,7 @@ It traverses the same sub-terms many times.
Outline of a presumably better scheme:
1. maintain map FV: term -> bit-set where bitset reprsents set of free variables. Assume the number of variables is bounded.
1. maintain map FV: term -> bit-set where bitset represents set of free variables. Assume the number of variables is bounded.
FV is built from initial terms.
2. maintain parent: term -> term-list of parent occurrences.
3. repeat

View file

@ -17,45 +17,51 @@ Notes:
--*/
#pragma once
#define ATOMIC_CMD(CLS_NAME, NAME, DESCR, ACTION) \
#define ATOMIC_CMD(CLS_NAME, NAME, DESCR, ACTION) \
class CLS_NAME : public cmd { \
public: \
CLS_NAME():cmd(NAME) {} \
char const * get_usage() const override { return 0; } \
char const * get_descr(cmd_context & ctx) const override { \
return DESCR; \
} \
unsigned get_arity() const override { return 0; } \
void execute(cmd_context & ctx) override { ACTION } \
}
#define UNARY_CMD(CLS_NAME, NAME, USAGE, DESCR, ARG_KIND, ARG_TYPE, ACTION) \
class CLS_NAME : public cmd { \
public: \
CLS_NAME():cmd(NAME) {} \
virtual char const * get_usage() const { return 0; } \
virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \
virtual unsigned get_arity() const { return 0; } \
virtual void execute(cmd_context & ctx) { ACTION } \
};
#define UNARY_CMD(CLS_NAME, NAME, USAGE, DESCR, ARG_KIND, ARG_TYPE, ACTION) \
class CLS_NAME : public cmd { \
public: \
CLS_NAME():cmd(NAME) {} \
virtual char const * get_usage() const { return USAGE; } \
virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \
virtual unsigned get_arity() const { return 1; } \
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return ARG_KIND; } \
virtual void set_next_arg(cmd_context & ctx, ARG_TYPE arg) { ACTION } \
char const * get_usage() const override { return USAGE; } \
char const * get_descr(cmd_context & ctx) const override { \
return DESCR; \
} \
unsigned get_arity() const override { return 1; } \
cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { \
return ARG_KIND; \
} \
void set_next_arg(cmd_context & ctx, ARG_TYPE arg) override { ACTION } \
}
// Macro for creating commands where the first argument is a symbol
// The second argument cannot be a symbol
#define BINARY_SYM_CMD(CLS_NAME, NAME, USAGE, DESCR, ARG_KIND, ARG_TYPE, ACTION) \
class CLS_NAME : public cmd { \
symbol m_sym; \
symbol m_sym; \
public: \
CLS_NAME():cmd(NAME) {} \
virtual char const * get_usage() const { return USAGE; } \
virtual char const * get_descr(cmd_context & ctx) const { return DESCR; } \
virtual unsigned get_arity() const { return 2; } \
virtual void prepare(cmd_context & ctx) { m_sym = symbol::null; } \
virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { \
return m_sym == symbol::null ? CPK_SYMBOL : ARG_KIND; \
char const * get_usage() const override { return USAGE; } \
char const * get_descr(cmd_context & ctx) const override { return DESCR; } \
unsigned get_arity() const override { return 2; } \
void prepare(cmd_context & ctx) override { m_sym = symbol::null; } \
cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { \
return m_sym == symbol::null ? CPK_SYMBOL : ARG_KIND; \
} \
virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_sym = s; } \
virtual void set_next_arg(cmd_context & ctx, ARG_TYPE arg) { ACTION } \
};
void set_next_arg(cmd_context & ctx, symbol const & s) override { m_sym = s; } \
void set_next_arg(cmd_context & ctx, ARG_TYPE arg) override { ACTION } \
}
class ast;
class expr;

View file

@ -7,7 +7,6 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "ast/arith_decl_plugin.h"
#include "ast/array_decl_plugin.h"
@ -65,7 +64,8 @@ enum class token_kind {
slash_tok,
minus_tok,
at_tok,
lambda_tok
lambda_tok,
modal_tok
};
struct parse_error : public std::exception {
@ -87,6 +87,7 @@ struct token {
std::string text;
unsigned line = 1;
unsigned col = 1;
bool dquote = false; // true for double-quoted strings ("..."): TPTP distinct objects
};
class lexer {
@ -163,6 +164,7 @@ public:
if (peek() == '\'' || peek() == '"') {
char q = get();
t.kind = token_kind::str;
t.dquote = (q == '"');
while (!eof()) {
char c = get();
if (c == '\\' && !eof()) {
@ -252,7 +254,7 @@ public:
case '^': t.kind = token_kind::lambda_tok; return t;
case '{':
// Modal operators: {$box}, {$dia}, etc. — lex as identifier including braces
t.kind = token_kind::id;
t.kind = token_kind::modal_tok;
t.text.push_back(c);
while (!eof() && peek() != '}')
t.text.push_back(get());
@ -277,10 +279,10 @@ public:
};
struct parsed_type {
std::vector<sort*> domain;
ptr_vector<sort> domain;
sort* range = nullptr;
parsed_type(sort* s): range(s) {}
parsed_type(std::vector<sort*> const& d, sort* r): domain(d), range(r) {}
parsed_type(ptr_vector<sort> const& d, sort* r): domain(d), range(r) {}
};
class tptp_parser {
@ -290,13 +292,20 @@ class tptp_parser {
array_util m_array;
sort* m_univ;
bool m_has_conjecture = false;
unsigned m_dropped_formulas = 0; // axioms/definitions skipped due to encoding errors
bool m_last_name_quoted = false;
bool m_last_name_dquoted = false; // last parsed name was a double-quoted distinct object
// Distinct objects: TPTP double-quoted strings ("...") denote pairwise distinct
// domain elements. Collected here (deduplicated by name) so a single global
// (distinct ...) constraint can be asserted before solving.
std::unordered_map<std::string, expr*> m_distinct_objects;
std::string m_expected_status; // SZS status from the input annotation, if any
std::unordered_map<std::string, sort*> m_sorts;
sort_ref_vector m_pinned_sorts; // prevents cached sorts from being freed
std::unordered_map<std::string, func_decl*> m_decls;
func_decl_ref_vector m_pinned_decls; // prevents cached func_decls from being freed
expr_ref_vector m_pinned_exprs; // prevents bound variable apps from being freed
std::unordered_map<std::string, std::pair<std::vector<sort*>, sort*>> m_typed_decls;
std::unordered_map<std::string, std::pair<ptr_vector<sort>, sort*>> m_typed_decls;
std::vector<std::unordered_map<std::string, app*>> m_bound;
bool m_in_at_arg = false; // true when parsing inside @ argument (lambda body stops consuming @)
struct implicit_var_scope {
@ -424,6 +433,7 @@ class tptp_parser {
std::string parse_name() {
if (is(token_kind::id) || is(token_kind::str)) {
m_last_name_quoted = is(token_kind::str);
m_last_name_dquoted = is(token_kind::str) && m_curr.dquote;
std::string r = m_curr.text;
next();
return r;
@ -449,7 +459,7 @@ class tptp_parser {
// For higher-order types like ($i > $o), create an uninterpreted sort
// Function type A > B is represented as Array(A, B).
// Multi-argument A * B > C is represented as Array(A, Array(B, C)) (curried).
sort* get_ho_sort(std::vector<sort*> const& domain, sort* range) {
sort* get_ho_sort(ptr_vector<sort> const& domain, sort* range) {
sort* s = range;
for (int i = (int)domain.size() - 1; i >= 0; --i)
s = m_array.mk_array_sort(domain[i], s);
@ -513,7 +523,8 @@ class tptp_parser {
if (itt != m_typed_decls.end()) {
std::string typed_decl_key = mk_decl_key(name, arity, 'd');
auto itd = m_decls.find(typed_decl_key);
if (itd != m_decls.end()) return itd->second;
if (itd != m_decls.end())
return itd->second;
auto const& sig = itt->second;
func_decl* f = m.mk_func_decl(symbol(name), sig.first.size(), sig.first.data(), sig.second);
m_pinned_decls.push_back(f);
@ -525,7 +536,7 @@ class tptp_parser {
auto itd = m_decls.find(key);
if (itd != m_decls.end()) return itd->second;
std::vector<sort*> dom(arity, m_univ);
ptr_vector<sort> dom(arity, m_univ);
func_decl* f = m.mk_func_decl(symbol(name), arity, dom.data(), pred ? m.mk_bool_sort() : m_univ);
m_pinned_decls.push_back(f);
m_decls.emplace(key, f);
@ -571,6 +582,14 @@ class tptp_parser {
expr_ref coerce_arg(expr_ref const& e, sort* target) {
sort* actual = e->get_sort();
if (actual == target) return e;
// Int <-> Real conversions must use the arithmetic semantics (to_real /
// to_int), never an uninterpreted boxing function: an uninterpreted box
// severs the numeric link between the two sides and lets the solver build
// spurious models (e.g. it would make floor/ceiling identities sat).
if (m_arith.is_int(actual) && m_arith.is_real(target))
return expr_ref(m_arith.mk_to_real(e), m);
if (m_arith.is_real(actual) && m_arith.is_int(target))
return expr_ref(m_arith.mk_to_int(e), m);
// Create a boxing function from actual sort to target sort
std::string box_name = std::string("$box_") + actual->get_name().str() + "_to_" + target->get_name().str();
std::string key = mk_decl_key(box_name, 1, 'f');
@ -697,14 +716,14 @@ class tptp_parser {
// <defined_type> ::= $oType | $o | $iType | $i | $tType | $real | $rat | $int
parsed_type parse_type_atom() {
if (accept(token_kind::lparen)) {
std::vector<sort*> prod = parse_type_product_raw();
ptr_vector<sort> prod = parse_type_product_raw();
if (accept(token_kind::gt_tok)) {
// Full function type inside parens: (A * B > C) or (A > B > C)
parsed_type rhs = parse_type_expr();
std::vector<sort*> full_domain = prod;
ptr_vector<sort> full_domain = prod;
if (!rhs.domain.empty()) {
// Nested higher-order: (A > B > C) → flatten
full_domain.insert(full_domain.end(), rhs.domain.begin(), rhs.domain.end());
full_domain.append(rhs.domain);
}
expect(token_kind::rparen, "')'");
// Return with domain/range preserved for proper flattening
@ -743,15 +762,15 @@ class tptp_parser {
// Grammar: <thf_xprod_type> ::= <thf_unitary_type> * <thf_unitary_type>
// | <thf_xprod_type> * <thf_unitary_type>
// Product types form the domain in mapping types: (A * B) > C
std::vector<sort*> parse_type_product_raw() {
ptr_vector<sort> parse_type_product_raw() {
parsed_type first = parse_type_atom();
if (!first.domain.empty() && first.range == nullptr) {
// Already a parenthesized product from nested parens
std::vector<sort*> args = first.domain;
ptr_vector<sort> args = first.domain;
while (accept(token_kind::star_tok)) {
parsed_type t = parse_type_atom();
if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end());
args.append(t.domain);
} else {
args.push_back(t.range);
}
@ -761,28 +780,28 @@ class tptp_parser {
if (!first.domain.empty()) {
// Function type as first element of product — use ho_sort
sort* ho = get_ho_sort(first.domain, first.range);
std::vector<sort*> args;
ptr_vector<sort> args;
args.push_back(ho);
while (accept(token_kind::star_tok)) {
parsed_type t = parse_type_atom();
if (!t.domain.empty() && t.range != nullptr) {
args.push_back(get_ho_sort(t.domain, t.range));
} else if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end());
args.append(t.domain);
} else {
args.push_back(t.range);
}
}
return args;
}
std::vector<sort*> args;
ptr_vector<sort> args;
args.push_back(first.range);
while (accept(token_kind::star_tok)) {
parsed_type t = parse_type_atom();
if (!t.domain.empty() && t.range != nullptr) {
args.push_back(get_ho_sort(t.domain, t.range));
} else if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end());
args.append(t.domain);
} else {
args.push_back(t.range);
}
@ -801,7 +820,7 @@ class tptp_parser {
return first;
}
// Build product vector
std::vector<sort*> args;
ptr_vector<sort> args;
if (!first.domain.empty() && first.range != nullptr) {
// Function type used as element in a product
args.push_back(get_ho_sort(first.domain, first.range));
@ -816,7 +835,7 @@ class tptp_parser {
if (!t.domain.empty() && t.range != nullptr) {
args.push_back(get_ho_sort(t.domain, t.range));
} else if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end());
args.append(t.domain);
} else {
args.push_back(t.range);
}
@ -833,7 +852,7 @@ class tptp_parser {
if (is(token_kind::type_forall_tok) || is(token_kind::type_exists_tok)) {
next();
expect(token_kind::lbrack, "'['");
std::vector<sort*> type_params;
ptr_vector<sort> type_params;
if (!accept(token_kind::rbrack)) {
do {
std::string tv = parse_name();
@ -848,8 +867,8 @@ class tptp_parser {
parsed_type inner = parse_type_expr();
// Prepend type params to domain
if (!type_params.empty()) {
std::vector<sort*> full_domain = type_params;
full_domain.insert(full_domain.end(), inner.domain.begin(), inner.domain.end());
ptr_vector<sort> full_domain = type_params;
full_domain.append(inner.domain);
return parsed_type(full_domain, inner.range);
}
return inner;
@ -858,7 +877,7 @@ class tptp_parser {
if (accept(token_kind::gt_tok)) {
parsed_type rhs = parse_type_expr();
// prod is either a product (domain non-empty, range==nullptr) or a single sort (domain empty)
std::vector<sort*> domain;
ptr_vector<sort> domain;
if (!prod.domain.empty() && prod.range == nullptr) {
domain = prod.domain;
} else if (!prod.domain.empty() && prod.range != nullptr) {
@ -869,7 +888,7 @@ class tptp_parser {
}
if (!rhs.domain.empty()) {
// Higher-order result type: A > (B > C) flattened to (A, B) > C
domain.insert(domain.end(), rhs.domain.begin(), rhs.domain.end());
domain.append(rhs.domain);
return parsed_type(domain, rhs.range);
}
return parsed_type(domain, rhs.range);
@ -925,7 +944,7 @@ class tptp_parser {
// Grammar: (same as parse_term, primary productions)
expr_ref parse_term_primary() {
if (accept(token_kind::lparen)) {
expr_ref e = parse_formula();
expr_ref e = parse_formula(false);
expect(token_kind::rparen, "')'");
return e;
}
@ -946,6 +965,7 @@ class tptp_parser {
return parse_numeral_from_name(n);
}
bool dq_name = m_last_name_dquoted;
expr_ref b(m);
// Check bound variables: uppercase (quantifier vars) AND lowercase (let-bound names)
if (!m_last_name_quoted && find_bound(n, b)) {
@ -971,11 +991,11 @@ class tptp_parser {
// $ite needs special parsing: first arg is formula, rest are formulas (branches can be equalities)
if (n == "$ite") {
expect(token_kind::lparen, "'('");
args.push_back(parse_formula());
args.push_back(parse_formula(true));
expect(token_kind::comma, "','");
args.push_back(parse_formula());
args.push_back(parse_formula(false));
expect(token_kind::comma, "','");
args.push_back(parse_formula());
args.push_back(parse_formula(false));
expect(token_kind::rparen, "')'");
}
else if (n == "$let") {
@ -995,14 +1015,17 @@ class tptp_parser {
}
func_decl* f = mk_decl_or_ho_const(n, args.size(), false);
if (!args.empty()) coerce_args(f, args);
return expr_ref(args.empty() ? m.mk_const(f) : m.mk_app(f, args.size(), args.data()), m);
coerce_args(f, args);
expr_ref term(args.empty() ? m.mk_const(f) : m.mk_app(f, args.size(), args.data()), m);
if (dq_name && args.empty())
register_distinct_object(n, term);
return term;
}
// Grammar: <tff_logic_formula> ::= <tff_unitary_formula> | <tff_binary_formula>
// <thf_logic_formula> ::= <thf_unitary_formula> | <thf_binary_formula>
// Entry point for formula parsing (wraps parse_expr with default precedence).
expr_ref parse_formula();
expr_ref parse_formula(bool is_boolean);
// Grammar: <thf_apply_formula> ::= <thf_unitary_formula> @ <thf_unitary_formula>
// | <thf_apply_formula> @ <thf_unitary_formula>
@ -1042,7 +1065,7 @@ class tptp_parser {
return parse_lambda_expr();
}
if (accept(token_kind::lparen)) {
expr_ref e = parse_formula();
expr_ref e = parse_formula(false);
expect(token_kind::rparen, "')'");
// Do NOT call apply_at here — outer apply_at owns the remaining @ tokens
return e;
@ -1074,7 +1097,7 @@ class tptp_parser {
// Quantifier body in @-arg should NOT consume @ — those belong to enclosing application
bool save_in_at_arg = m_in_at_arg;
m_in_at_arg = true;
expr_ref body = parse_formula();
expr_ref body = parse_formula(false);
m_in_at_arg = save_in_at_arg;
m_bound.pop_back();
return mk_quantifier(is_forall, vars, body);
@ -1092,7 +1115,7 @@ class tptp_parser {
std::string key = mk_decl_key(name_str, 0, 'c') + "\x1f" + std::to_string(range->get_id());
auto it = m_decls.find(key);
if (it != m_decls.end()) return it->second;
func_decl* f = m.mk_func_decl(name, 0, static_cast<sort**>(nullptr), range);
func_decl* f = m.mk_const_decl(name, range);
m_pinned_decls.push_back(f);
m_decls.emplace(key, f);
return f;
@ -1121,31 +1144,29 @@ class tptp_parser {
// Coerce two expressions to have the same sort for equality.
// In TPTP, = is term equality and m_univ is the default sort.
// If one side has Bool sort (parsed as predicate), coerce it to m_univ.
// If sorts already match and are not Bool, returns lhs unchanged.
// If sorts already match, returns lhs unchanged; otherwise applies minimal
// arithmetic/box coercions so both sides of an equality share a sort.
expr_ref coerce_eq(expr_ref lhs, expr_ref& rhs) {
// Coerce Bool-sorted operands to m_univ since = is term equality in TPTP
if (m.is_bool(lhs->get_sort()) && is_app(lhs) && !m.is_true(lhs) && !m.is_false(lhs))
lhs = coerce_to_univ(lhs);
if (m.is_bool(rhs->get_sort()) && is_app(rhs) && !m.is_true(rhs) && !m.is_false(rhs))
rhs = coerce_to_univ(rhs);
if (lhs->get_sort() == rhs->get_sort()) return lhs;
// No coercion is needed when both sides already share a sort. In particular
// `=` between two Boolean ($o) operands is logical equivalence (iff): keep
// them Boolean instead of forcing one into the term universe U.
if (lhs->get_sort() == rhs->get_sort())
return lhs;
if (m_arith.is_int_real(lhs) && m_arith.is_int_real(rhs))
return lhs;
// Coerce 0-arity constants to match the other side's sort
if (is_app(lhs) && to_app(lhs)->get_num_args() == 0 && lhs->get_sort() != rhs->get_sort()) {
if (is_app(lhs) && to_app(lhs)->get_num_args() == 0) {
return coerce_zero_arity(to_app(lhs), rhs->get_sort());
}
if (is_app(rhs) && to_app(rhs)->get_num_args() == 0 && lhs->get_sort() != rhs->get_sort()) {
if (is_app(rhs) && to_app(rhs)->get_num_args() == 0) {
rhs = coerce_zero_arity(to_app(rhs), lhs->get_sort());
return lhs;
}
// Last resort: coerce both sides to have the same sort
if (lhs->get_sort() != rhs->get_sort()) {
// Prefer coercing to rhs sort, falling back to m_univ
sort* target = rhs->get_sort();
lhs = coerce_arg(lhs, target);
}
// Prefer coercing to rhs sort, falling back to m_univ
sort* target = rhs->get_sort();
lhs = coerce_arg(lhs, target);
return lhs;
}
@ -1176,7 +1197,7 @@ class tptp_parser {
// Bind parameter variables for parsing the RHS
if (!param_scope.empty())
m_bound.push_back(param_scope);
expr_ref value = parse_formula();
expr_ref value = parse_formula(false);
if (!param_scope.empty())
m_bound.pop_back();
// For function-style definitions, wrap value in lambdas
@ -1206,7 +1227,7 @@ class tptp_parser {
// --- Part 1: Parse type declarations ---
std::vector<std::string> let_names;
std::vector<sort*> let_sorts;
ptr_vector<sort> let_sorts;
auto parse_one_typing = [&]() {
std::string name = parse_name();
@ -1264,7 +1285,7 @@ class tptp_parser {
// --- Part 3: Parse body with let-bound names in scope ---
m_bound.push_back(scope);
expr_ref body = parse_formula();
expr_ref body = parse_formula(false);
m_bound.pop_back();
expect(token_kind::rparen, "')'");
@ -1288,7 +1309,7 @@ class tptp_parser {
// <defined_pred> ::= $less | $lesseq | $greater | $greatereq | $is_int | $is_rat | ...
// <defined_infix_pred> ::= = | !=
// Also handles: let-bound name resolution, implicit variable creation.
expr_ref parse_atomic_formula() {
expr_ref parse_atomic_formula(bool is_boolean) {
if (accept(token_kind::lparen)) {
// Check for parenthesized connective used as higher-order term: (~), (&), (|), etc.
if (is(token_kind::not_tok) || is(token_kind::and_tok) || is(token_kind::or_tok) ||
@ -1307,31 +1328,50 @@ class tptp_parser {
token saved = m_curr;
next();
if (accept(token_kind::rparen)) {
// Parenthesized connective: treat as HO constant with array sort
// A parenthesized connective used as a higher-order term, e.g.
// "(~) @ p" or "(|) @ p @ q". Encode it as a genuine lambda over Bool
// carrying the real logical semantics, so that application beta-reduces
// to the actual connective (e.g. "(~) @ p" ==> "not p"). Encoding it as
// an uninterpreted array constant instead would sever it from Boolean
// logic and make valid higher-order theorems spuriously
// CounterSatisfiable (the (~)/(|) applications would be unrelated to the
// truth values of their operands).
(void)op_text;
sort* bool_sort = m.mk_bool_sort();
sort* ho_sort;
if (arity == 1)
ho_sort = m_array.mk_array_sort(bool_sort, bool_sort);
else
ho_sort = m_array.mk_array_sort(bool_sort, m_array.mk_array_sort(bool_sort, bool_sort));
std::string key = mk_decl_key(op_text, 0, 'h');
auto it = m_decls.find(key);
func_decl* f;
if (it != m_decls.end()) {
f = it->second;
} else {
f = m.mk_func_decl(symbol(op_text), 0, static_cast<sort**>(nullptr), ho_sort);
m_pinned_decls.push_back(f);
m_decls.emplace(key, f);
symbol xn("X"), yn("Y");
if (arity == 1) {
// (~) ==> ^[X:$o] : ~X
expr_ref body(m.mk_not(m.mk_var(0, bool_sort)), m);
return expr_ref(m.mk_lambda(1, &bool_sort, &xn, body), m);
}
return expr_ref(m.mk_const(f), m);
// binary connective ==> ^[X:$o] : ^[Y:$o] : (X <op> Y)
// de Bruijn: X is var(1) (outer binder), Y is var(0) (inner binder).
expr* vx = m.mk_var(1, bool_sort);
expr* vy = m.mk_var(0, bool_sort);
expr_ref opbody(m);
switch (saved.kind) {
case token_kind::and_tok: opbody = m.mk_and(vx, vy); break;
case token_kind::or_tok: opbody = m.mk_or(vx, vy); break;
case token_kind::implies_tok: opbody = m.mk_implies(vx, vy); break;
case token_kind::iff_tok: opbody = m.mk_eq(vx, vy); break;
case token_kind::xor_tok: opbody = m.mk_xor(vx, vy); break;
default: opbody = m.mk_eq(vx, vy); break;
}
expr_ref inner(m.mk_lambda(1, &bool_sort, &yn, opbody), m);
return expr_ref(m.mk_lambda(1, &bool_sort, &xn, inner), m);
}
// Not a parenthesized connective — lparen was consumed and connective was consumed
// but ')' didn't follow. Parse as formula with the connective already consumed.
expr_ref inner(m);
if (saved.kind == token_kind::not_tok) {
expr_ref e = parse_formula();
inner = expr_ref(m.mk_not(e), m);
// "( ~ <formula> )": the '~' is a unary connective binding only the
// next unary unit; ordinary binary connectives then apply at their
// own precedence (e.g. "( ~ p | q )" is "(~p) | q", NOT "~(p | q)").
// We have already consumed '(' and '~', so negate the next unit and
// resume precedence-climbing parsing from that negated left operand.
expr_ref operand = parse_unary_formula(true);
expr_ref neg(m.mk_not(ensure_bool(operand)), m);
inner = parse_binary_rest(neg, PREC_IFF, true);
} else {
// Binary connective at start of parens — shouldn't happen in valid TPTP
throw parse_error("unexpected connective after '(' at " + loc());
@ -1342,12 +1382,15 @@ class tptp_parser {
// Parentheses create a new scope for @ consumption
bool save_in_at_arg = m_in_at_arg;
m_in_at_arg = false;
expr_ref e = parse_formula();
expr_ref e = parse_formula(is_boolean);
expect(token_kind::rparen, "')'");
m_in_at_arg = save_in_at_arg;
return e;
}
if (accept(token_kind::modal_tok))
throw parse_error("modal operators not supported in TPTP input at " + loc());
// Handle negative numerals in formula position: -2 = $uminus(2)
if (accept(token_kind::minus_tok)) {
expr_ref t = parse_term();
@ -1358,9 +1401,9 @@ class tptp_parser {
if (accept(token_kind::lbrack)) {
if (accept(token_kind::rbrack))
return expr_ref(m.mk_const(symbol("$nil"), m_univ), m);
expr_ref first = parse_formula();
expr_ref first = parse_formula(is_boolean);
while (accept(token_kind::comma))
parse_formula(); // consume remaining elements
parse_formula(is_boolean); // consume remaining elements
expect(token_kind::rbrack, "']'");
return first;
}
@ -1373,6 +1416,7 @@ class tptp_parser {
return parse_numeral_from_name(n);
}
bool dq_name = m_last_name_dquoted;
// Check if name is let-bound (works for both uppercase vars and lowercase let-bound names)
{
expr_ref b(m);
@ -1417,7 +1461,7 @@ class tptp_parser {
}
expect(token_kind::colon, "':'");
m_bound.push_back(scope);
expr_ref body = parse_formula();
expr_ref body = parse_formula(is_boolean);
m_bound.pop_back();
// Approximate choice as existential quantification
return mk_quantifier(false, vars, body);
@ -1427,11 +1471,11 @@ class tptp_parser {
// $ite needs special parsing: first arg is formula, rest are formulas (branches can be equalities)
if (n == "$ite") {
expect(token_kind::lparen, "'('");
args.push_back(parse_formula());
args.push_back(parse_formula(true));
expect(token_kind::comma, "','");
args.push_back(parse_formula());
args.push_back(parse_formula(is_boolean));
expect(token_kind::comma, "','");
args.push_back(parse_formula());
args.push_back(parse_formula(is_boolean));
expect(token_kind::rparen, "')'");
}
else if (n == "$let") {
@ -1465,18 +1509,19 @@ class tptp_parser {
auto typed = m_typed_decls.find(mk_typed_key(n, args.size()));
if (typed != m_typed_decls.end()) {
func_decl* f = args.empty() ? mk_decl_or_ho_const(n, 0, false) : mk_decl(n, args.size(), false);
if (!args.empty()) coerce_args(f, args);
return expr_ref(args.empty() ? m.mk_const(f) : m.mk_app(f, args.size(), args.data()), m);
coerce_args(f, args);
return expr_ref(m.mk_app(f, args.size(), args.data()), m);
}
if (args.empty() && (is(token_kind::equal_tok) || is(token_kind::neq_tok))) {
func_decl* f = mk_decl_or_ho_const(n, 0, false);
return expr_ref(m.mk_const(f), m);
}
is_boolean = is_boolean && !is(token_kind::equal_tok) && !is(token_kind::neq_tok);
func_decl* pred = mk_decl_or_ho_const(n, args.size(), true);
if (!args.empty()) coerce_args(pred, args);
return expr_ref(args.empty() ? m.mk_const(pred) : m.mk_app(pred, args.size(), args.data()), m);
func_decl* pred = mk_decl_or_ho_const(n, args.size(), is_boolean);
coerce_args(pred, args);
expr_ref atom(m.mk_app(pred, args.size(), args.data()), m);
if (dq_name && args.empty() && !m.is_bool(atom))
register_distinct_object(n, atom);
return atom;
}
// Grammar: <thf_abstraction> ::= ^ [<thf_variable_list>] : <thf_unitary_formula>
@ -1513,7 +1558,7 @@ class tptp_parser {
// Lambda body does NOT consume @ — @ belongs to the enclosing application
bool save_in_at_arg = m_in_at_arg;
m_in_at_arg = true;
expr_ref body = parse_formula();
expr_ref body = parse_formula(false);
m_in_at_arg = save_in_at_arg;
m_bound.pop_back();
if (vars.empty())
@ -1538,9 +1583,9 @@ class tptp_parser {
// <thf_quantified_formula> ::= <thf_quantification> <thf_unitary_formula>
// <fof_quantifier> ::= ! | ?
// Also handles: $ite, $let, lambda (^), parenthesized formulas, and atomic formulas.
expr_ref parse_unary_formula() {
expr_ref parse_unary_formula(bool is_boolean) {
if (accept(token_kind::not_tok)) {
expr_ref e = parse_unary_formula();
expr_ref e = parse_unary_formula(true);
return expr_ref(m.mk_not(ensure_bool(e)), m);
}
@ -1552,7 +1597,7 @@ class tptp_parser {
next(); // consume '['
if (accept(token_kind::dot)) {
expect(token_kind::rbrack, "']'");
expr_ref sub = parse_unary_formula();
expr_ref sub = parse_unary_formula(is_boolean);
func_decl* f = mk_modal_op("box");
return expr_ref(m.mk_app(f, sub.get()), m);
}
@ -1561,7 +1606,7 @@ class tptp_parser {
std::string first_name = m_curr.text;
next();
if (accept(token_kind::rbrack)) {
expr_ref sub = parse_unary_formula();
expr_ref sub = parse_unary_formula(is_boolean);
func_decl* f = mk_modal_op(mod_name);
return expr_ref(m.mk_app(f, sub.get()), m);
}
@ -1575,11 +1620,11 @@ class tptp_parser {
else if (should_create_implicit_var(first_name))
first = expr_ref(get_or_create_implicit_var(first_name), m);
else {
func_decl* f = mk_decl_or_ho_const(first_name, 0, false);
func_decl* f = mk_decl_or_ho_const(first_name, 0, is_boolean);
first = expr_ref(m.mk_const(f), m);
}
while (accept(token_kind::comma))
parse_formula(); // consume remaining elements
parse_formula(is_boolean); // consume remaining elements
expect(token_kind::rbrack, "']'");
return first;
}
@ -1587,9 +1632,9 @@ class tptp_parser {
// We already consumed '[', so parse as tuple inline
if (accept(token_kind::rbrack))
return expr_ref(m.mk_const(symbol("$nil"), m_univ), m);
expr_ref first = parse_formula();
expr_ref first = parse_formula(is_boolean);
while (accept(token_kind::comma))
parse_formula(); // consume remaining elements
parse_formula(is_boolean); // consume remaining elements
expect(token_kind::rbrack, "']'");
return first;
}
@ -1605,7 +1650,7 @@ class tptp_parser {
next();
}
expect(token_kind::gt_tok, "'>'");
expr_ref sub = parse_unary_formula();
expr_ref sub = parse_unary_formula(is_boolean);
func_decl* f = mk_modal_op(mod_name);
return expr_ref(m.mk_app(f, sub.get()), m);
}
@ -1652,7 +1697,13 @@ class tptp_parser {
}
expect(token_kind::colon, "':'");
m_bound.push_back(scope);
expr_ref body = parse_formula();
// A TPTP quantifier body is a <unit_formula>: the quantifier binds
// tighter than the binary connectives & | => <= <=> <~> ~| ~&. Parse
// at equality precedence so the body absorbs an infix =/!= but stops
// at any lower-precedence connective, which stays in the enclosing
// expression. E.g. "! [X] : p(X) & q(X)" is "(! [X] : p(X)) & q(X)",
// and "! [X] : (...) => g" keeps "=> g" outside the quantifier scope.
expr_ref body = parse_expr(PREC_EQ, true, is_boolean);
m_bound.pop_back();
return mk_quantifier(is_forall, vars, body);
}
@ -1672,10 +1723,10 @@ class tptp_parser {
expect(token_kind::rbrack, "']'");
}
expect(token_kind::colon, "':'");
return parse_formula();
return parse_formula(is_boolean);
}
return parse_atomic_formula();
return parse_atomic_formula(is_boolean);
}
// Grammar: <tff_binary_formula> ::= <tff_binary_nonassoc> | <tff_binary_assoc>
@ -1687,8 +1738,15 @@ class tptp_parser {
// <tff_and_formula> ::= <tff_unit_formula> & <tff_unit_formula>
// | <tff_and_formula> & <tff_unit_formula>
// Implements a Pratt-style (precedence climbing) parser for binary connectives.
expr_ref parse_expr(unsigned min_prec, bool consume_at = true) {
expr_ref e = parse_unary_formula();
expr_ref parse_expr(unsigned min_prec, bool consume_at, bool is_boolean) {
expr_ref e = parse_unary_formula(is_boolean);
return parse_binary_rest(e, min_prec, consume_at);
}
// Precedence-climbing loop continued from an already-parsed left operand `e`.
// Split out from parse_expr so callers that have consumed a leading unary unit
// (e.g. a '~' immediately after '(') can resume binary-connective parsing.
expr_ref parse_binary_rest(expr_ref e, unsigned min_prec, bool consume_at = true) {
for (;;) {
// Handle @ (function application) with highest precedence
// But NOT when we're inside a lambda body that's an @ argument
@ -1719,7 +1777,15 @@ class tptp_parser {
if (it->second.precedence < min_prec) break;
next(); // consume the operator token
unsigned next_prec = it->second.right_assoc ? it->second.precedence : it->second.precedence + 1;
expr_ref rhs = parse_expr(next_prec, consume_at);
// Operands of every connective except '='/'!=' are Boolean; only equality
// takes term operands. Derive the operand context from the operator rather
// than inheriting is_boolean from the left operand. Otherwise, once a
// term-valued equality literal (e.g. 'a = b') sets is_boolean to false, a
// predicate atom later in the same clause ('a = b | q') would be parsed as a
// term and boxed into '$box_U_to_Bool(q)', severing it from the Boolean
// predicate 'q' used elsewhere and making refutable problems satisfiable.
bool rhs_is_boolean = it->second.precedence != PREC_EQ;
expr_ref rhs = parse_expr(next_prec, consume_at, rhs_is_boolean);
expr_ref_vector args(m);
args.push_back(e);
args.push_back(rhs);
@ -1786,12 +1852,27 @@ class tptp_parser {
// Try relative to current file's directory
std::string local = normalize_path(dirname(curr_file) + "/" + name);
if (file_exists(local)) return local;
// Try TPTP environment variable (standard TPTP convention)
// Try TPTP environment variable (standard TPTP convention): includes such as
// "Axioms/MAT001^0.ax" are resolved relative to the TPTP root directory named
// by $TPTP. This is required when a problem is run from a directory that does
// not contain the Axioms/ tree (e.g. an isolated benchmark harness workspace).
char const* root = std::getenv("TPTP");
if (root) {
std::string env = normalize_path(std::string(root) + "/" + name);
if (file_exists(env)) return env;
}
// Walk up ancestor directories of the current file. TPTP include paths are
// relative to the TPTP root directory (e.g. "Axioms/BOO001-0.ax"), while the
// problem file typically lives in a subdirectory such as "Problems/BOO/".
std::string dir = dirname(curr_file);
for (;;) {
size_t idx = dir.find_last_of("/\\");
if (idx == std::string::npos) break;
dir = dir.substr(0, idx);
if (dir.empty()) break;
std::string candidate = normalize_path(dir + "/" + name);
if (file_exists(candidate)) return candidate;
}
// Try relative to current working directory (common when running from TPTP root)
std::string cwd_relative = normalize_path(name);
if (file_exists(cwd_relative)) return cwd_relative;
@ -1825,7 +1906,7 @@ class tptp_parser {
// <annotations> ::= ,<source><optional_info> | <null>
void parse_annotated() {
expect(token_kind::lparen, "'('");
parse_name();
std::string formula_name = parse_name();
expect(token_kind::comma, "','");
std::string role = to_lower(parse_name());
expect(token_kind::comma, "','");
@ -1836,12 +1917,14 @@ class tptp_parser {
else if (role == "logic") {
// Modal logic declarations ($modal == [...]) — skip the formula body
skip_annotations_until_rparen();
warning_msg("non-classical logics are not supported");
++m_dropped_formulas;
}
else {
try {
implicit_var_scope implicit_scope;
scoped_implicit_vars scoped(*this, implicit_scope);
expr_ref f = parse_formula();
expr_ref f = parse_formula(true);
if (!implicit_scope.order.empty()) {
f = mk_quantifier(true, implicit_scope.order, f);
}
@ -1854,13 +1937,30 @@ class tptp_parser {
}
m_cmd.assert_expr(f);
} catch (z3_exception const& ex) {
// Sort mismatch or other semantic error in this formula — skip it
IF_VERBOSE(2, verbose_stream() << "skipping formula due to: " << ex.what() << "\n");
// Sort mismatch or other semantic error in this formula — skip it.
// A dropped axiom/definition removes constraints from the problem, so a
// subsequent "sat" verdict is unsound: it may only hold because the
// dropped formula was missing. The count is used to downgrade a sat
// result to GaveUp rather than report a spurious CounterSatisfiable.
++m_dropped_formulas;
std::ostringstream oss;
oss << "skipping formula '" << formula_name << "' due to: " << ex.what();
warning_msg(oss.str().c_str());
// Skip to '.' to resync the parser for the next annotated formula
while (!is(token_kind::eof_tok) && !is(token_kind::dot))
next();
if (is(token_kind::dot)) next();
return;
} catch (std::exception const& ex) {
++m_dropped_formulas;
std::ostringstream oss;
oss << "skipping formula '" << formula_name << "' (role " << role << ") due to: " << ex.what() << "\n";
warning_msg(oss.str().c_str());
while (!is(token_kind::eof_tok) && !is(token_kind::dot))
next();
if (is(token_kind::dot))
next();
return;
}
}
@ -2114,6 +2214,12 @@ public:
}};
}
// Record a double-quoted string constant as a TPTP distinct object (deduplicated by name).
void register_distinct_object(std::string const& name, expr* c) {
if (m_distinct_objects.emplace(name, c).second)
m_pinned_exprs.push_back(c);
}
void parse_input(std::istream& in, std::string const& current_file) {
// Save parser state so that included files don't clobber the caller's lexer.
std::string saved_input = std::move(m_input);
@ -2123,6 +2229,7 @@ public:
std::ostringstream buf;
buf << in.rdbuf();
m_input = buf.str();
extract_expected_status(m_input);
m_lex = std::make_unique<lexer>(m_input);
next();
parse_toplevel(current_file);
@ -2149,6 +2256,78 @@ public:
}
bool has_conjecture() const { return m_has_conjecture; }
// TPTP double-quoted strings ("...") denote pairwise distinct domain elements.
// Assert a single global distinctness constraint over all collected distinct objects
// so that e.g. "Apple" != "Microsoft" is recognized as a theorem.
void assert_distinct_objects() {
if (m_distinct_objects.size() < 2) return;
expr_ref_vector objs(m);
for (auto const& kv : m_distinct_objects)
objs.push_back(kv.second);
m_cmd.assert_expr(expr_ref(m.mk_distinct(objs.size(), objs.data()), m));
}
// Number of axioms/definitions that were dropped during parsing because the
// higher-order encoding could not type-check them. When non-zero, a "sat"
// verdict cannot be trusted (the missing constraints may be exactly what
// makes the problem unsatisfiable).
unsigned dropped_formulas() const { return m_dropped_formulas; }
std::string const& expected_status() const { return m_expected_status; }
// Scan TPTP comments for an SZS/Status annotation, e.g.
// % Status : Unsatisfiable
// % SZS status Theorem
// Only the first annotation found (the top-level file's) is recorded.
void extract_expected_status(std::string const& text) {
if (!m_expected_status.empty())
return;
std::istringstream in(text);
std::string line;
while (std::getline(in, line)) {
// TPTP comment lines start with '%'.
size_t i = line.find_first_not_of(" \t");
if (i == std::string::npos || line[i] != '%')
continue;
++i; // skip '%'
// Skip leading '%' and spaces.
i = line.find_first_not_of("% \t", i);
if (i == std::string::npos)
continue;
std::string rest = line.substr(i);
std::string status;
// Form 1: "SZS status <Word>"
if (rest.compare(0, 4, "SZS ") == 0) {
size_t p = rest.find("status");
if (p == std::string::npos)
continue;
p += 6; // length of "status"
status = next_status_word(rest, p);
}
// Form 2: "Status : <Word>" / "Status : <Word>"
else if (rest.compare(0, 6, "Status") == 0) {
size_t p = rest.find(':', 6);
if (p == std::string::npos)
continue;
status = next_status_word(rest, p + 1);
}
if (!status.empty()) {
m_expected_status = status;
return;
}
}
}
static std::string next_status_word(std::string const& s, size_t p) {
size_t a = s.find_first_not_of(" \t", p);
if (a == std::string::npos)
return "";
size_t b = a;
while (b < s.size() && (isalnum((unsigned char)s[b]) || s[b] == '_'))
++b;
return s.substr(a, b - a);
}
};
expr_ref tptp_parser::parse_term() {
@ -2172,12 +2351,41 @@ expr_ref tptp_parser::parse_term() {
return e;
}
expr_ref tptp_parser::parse_formula() {
return parse_expr(PREC_IFF);
expr_ref tptp_parser::parse_formula(bool is_boolean) {
return parse_expr(PREC_IFF, true, is_boolean);
}
}
// Classify an SZS status into the coarse verdict used for cross-checking.
// unsat: a refutation/proof exists (Theorem, Unsatisfiable, ContradictoryAxioms, ...)
// sat: a model exists (Satisfiable, CounterSatisfiable, ...)
// other: no comparable verdict (Open, Unknown, Timeout, GaveUp, empty, ...)
enum class szs_verdict { unsat, sat, other };
static szs_verdict classify_szs(std::string const& s) {
if (s == "Theorem" || s == "Unsatisfiable" || s == "ContradictoryAxioms" || s == "Unsat")
return szs_verdict::unsat;
if (s == "Satisfiable" || s == "CounterSatisfiable" || s == "CounterTheorem" || s == "Sat")
return szs_verdict::sat;
return szs_verdict::other;
}
// Emit the SZS status produced by z3. If the input carries an annotated status
// that contradicts the produced verdict, prepend "BUG" to flag the mismatch.
static void report_szs_status(char const* produced, std::string const& expected) {
szs_verdict pv = classify_szs(produced);
szs_verdict ev = classify_szs(expected);
bool is_bug = !expected.empty() &&
(pv == szs_verdict::unsat || pv == szs_verdict::sat) &&
(ev == szs_verdict::unsat || ev == szs_verdict::sat) &&
pv != ev;
if (is_bug)
std::cout << "% SZS status BUG " << produced << " (expected " << expected << ")\n";
else
std::cout << "% SZS status " << produced << "\n";
}
static unsigned read_tptp_stream(std::istream& in, char const* current_file) {
register_on_timeout_proc(on_timeout);
try {
@ -2186,6 +2394,7 @@ static unsigned read_tptp_stream(std::istream& in, char const* current_file) {
tptp_parser p(ctx);
p.parse_input(in, current_file ? current_file : ".");
p.assert_distinct_objects();
// Suppress default check-sat output; TPTP frontend reports SZS status explicitly.
std::ostringstream sink;
@ -2194,12 +2403,24 @@ static unsigned read_tptp_stream(std::istream& in, char const* current_file) {
ctx.check_sat(0, nullptr);
switch (ctx.cs_state()) {
case cmd_context::css_unsat:
if (p.has_conjecture()) std::cout << "% SZS status Theorem\n";
else std::cout << "% SZS status Unsatisfiable\n";
if (p.has_conjecture()) report_szs_status("Theorem", p.expected_status());
else report_szs_status("Unsatisfiable", p.expected_status());
break;
case cmd_context::css_sat:
if (p.has_conjecture()) std::cout << "% SZS status CounterSatisfiable\n";
else std::cout << "% SZS status Satisfiable\n";
// A "sat" verdict is only sound if the whole problem was encoded. If any
// axiom/definition was dropped during parsing (e.g. an unsupported
// higher-order construct), the model may be spurious — the dropped
// constraints could rule it out. Report GaveUp instead of a misleading
// CounterSatisfiable/Satisfiable (which would otherwise be flagged BUG
// against an annotated Theorem/Unsatisfiable status).
if (p.dropped_formulas() > 0) {
std::cout << "% SZS status GaveUp\n";
std::cout << "% SZS reason " << p.dropped_formulas()
<< " formula(s) dropped during encoding; model is not certified\n";
break;
}
if (p.has_conjecture()) report_szs_status("CounterSatisfiable", p.expected_status());
else report_szs_status("Satisfiable", p.expected_status());
if (g_display_model) {
model_ref mdl;
if (ctx.is_model_available(mdl))

View file

@ -297,7 +297,7 @@ namespace lp {
void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict) {
auto* lar = &m_bp.lp();
const auto& row = this->m_row;
auto* row = &this->m_row;
auto explain = [row, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, lar]() {
(void) strict;
TRACE(bound_analyzer, tout << "explain_bound_on_var_on_coeff, bound_j = " << bound_j << ", coeff_before_j_is_pos = " << coeff_before_j_is_pos << ", is_lower_bound = " << is_lower_bound << ", strict = " << strict << "\n";);
@ -305,7 +305,7 @@ namespace lp {
int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign;
u_dependency* ret = nullptr;
for (auto const& r : row) {
for (auto const& r : *row) {
unsigned j = r.var();
if (j == bound_j)
continue;

View file

@ -580,7 +580,7 @@ namespace lp {
const lar_term* m_t;
undo_add_term(imp& s, const lar_term* t) : m_s(s), m_t(t) {}
void undo() {
void undo() override {
m_s.undo_add_term_method(m_t);
}
};

View file

@ -16,6 +16,9 @@ Author:
Revision History:
--*/
#include <algorithm>
#include <unordered_map>
#include "math/lp/int_solver.h"
#include "math/lp/lar_solver.h"
#include "math/lp/int_cube.h"
@ -81,32 +84,264 @@ namespace lp {
SASSERT(lp_status::OPTIMAL == lra.get_status() || lp_status::FEASIBLE == lra.get_status());
}
impq int_cube::get_cube_delta_for_term(const lar_term& t) const {
if (t.size() == 2) {
bool seen_minus = false;
bool seen_plus = false;
for(lar_term::ival p : t) {
if (!lia.column_is_int(p.j()))
goto usual_delta;
const mpq & c = p.coeff();
if (c == one_of_type<mpq>()) {
seen_plus = true;
} else if (c == -one_of_type<mpq>()) {
seen_minus = true;
} else {
goto usual_delta;
// The largest cube test of Bromberger and Weidenbach:
// maximize x_e subject to Ax + a'(x_e/2) <= b, x_e >= 0, where a'_i = ||a_i||_1,
// with the 1-norm taken over the integer variables of the row.
// The solution is the center z of a largest cube contained in the polyhedron.
// If the maximal edge length is at least 1, then the rounding of z is
// an integer solution; otherwise the rounding is checked, and possibly repaired,
// against the original constraints.
lia_move int_cube::find_largest_cube() {
lia.settings().stats().m_lcube_calls++;
TRACE(cube,
for (unsigned j = 0; j < lra.number_of_vars(); ++j)
lia.display_column(tout, j);
tout << lra.constraints();
);
lra.push();
// The edge rows are ephemeral: suppress the add-term callback,
// dioph_eq's reaction to it is not undone by pop().
auto add_term_cb = lra.m_add_term_callback;
lra.m_add_term_callback = nullptr;
unsigned x_e = lra.add_var(UINT_MAX, false); // the edge length of the cube
lra.add_var_bound(x_e, lconstraint_kind::GE, mpq(0));
bool ok = add_cube_edge_rows(x_e);
lra.m_add_term_callback = add_term_cb;
if (!ok) {
lra.pop();
lra.set_status(lp_status::OPTIMAL);
return lia_move::undef;
}
lp_status st = lra.find_feasible_solution();
if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) {
TRACE(cube, tout << "cannot find a feasible solution";);
lra.pop();
lra.move_non_basic_columns_to_bounds();
// it can happen that we found an integer solution here
return !lra.r_basis_has_inf_int()? lia_move::sat: lia_move::undef;
}
impq e; // the maximal edge length
st = lra.maximize_term(x_e, e, /*fix_int_cols*/ false);
if (lia.settings().get_cancel_flag()) {
lra.pop();
return lia_move::undef;
}
if (st == lp_status::UNBOUNDED) {
// infinite lattice width: the polyhedron contains cubes of arbitrary edge length
lra.add_var_bound(x_e, lconstraint_kind::GE, mpq(1));
st = lra.find_feasible_solution();
if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) {
lra.pop();
return lia_move::undef;
}
lra.pop();
return sat_after_rounding();
}
TRACE(cube, tout << "max edge length = " << e << "\n";);
if (e >= impq(mpq(1))) {
lra.pop();
return sat_after_rounding();
}
// the largest cube is smaller than the unit cube:
// the rounded center is only a candidate
lra.pop();
return round_and_repair();
}
bool int_cube::add_cube_edge_rows(unsigned x_e) {
// snapshot the term columns: add_edge_rows_for_term appends to lra.terms()
svector<unsigned> term_columns;
for (const lar_term* t : lra.terms())
term_columns.push_back(t->j());
for (unsigned j : term_columns)
if (!add_edge_rows_for_term(j, x_e)) {
TRACE(cube, tout << "cannot add the edge rows";);
return false;
}
return true;
}
// i is the column index having the term
bool int_cube::add_edge_rows_for_term(unsigned i, unsigned x_e) {
if (!lra.column_associated_with_row(i))
return true;
const lar_term& t = lra.get_term(i);
impq delta = get_cube_delta_for_term(t);
TRACE(cube, lra.print_term_as_indices(t, tout); tout << ", delta = " << delta << "\n";);
if (is_zero(delta))
return true;
if (!is_zero(delta.y))
// the infinitesimal delta does not scale with x_e: tighten statically,
// it is sound for any edge length
return lra.tighten_term_bounds_by_delta(i, delta);
if (lra.column_has_upper_bound(i)) {
impq u = lra.get_upper_bound(i); // copy: add_term invalidates bound references
vector<std::pair<mpq, unsigned>> coeffs = {{mpq(1), i}, {delta.x, x_e}};
unsigned s = lra.add_term(coeffs, UINT_MAX);
lra.add_var_bound(s, is_zero(u.y) ? lconstraint_kind::LE : lconstraint_kind::LT, u.x);
}
if (lra.column_has_lower_bound(i)) {
impq l = lra.get_lower_bound(i); // copy: add_term invalidates bound references
vector<std::pair<mpq, unsigned>> coeffs = {{mpq(1), i}, {-delta.x, x_e}};
unsigned s = lra.add_term(coeffs, UINT_MAX);
lra.add_var_bound(s, is_zero(l.y) ? lconstraint_kind::GE : lconstraint_kind::GT, l.x);
}
return true;
}
lia_move int_cube::sat_after_rounding() {
lra.round_to_integer_solution();
lra.set_status(lp_status::FEASIBLE);
SASSERT(lia.settings().get_cancel_flag() || lia.is_feasible());
TRACE(cube, tout << "largest cube success";);
lia.settings().stats().m_lcube_success++;
return lia_move::sat;
}
lia_move int_cube::round_and_repair() {
lra.backup_x(); // remember the cube center
vector<flip_candidate> flips;
for (unsigned j = 0; j < lra.column_count(); ++j) {
if (!lra.column_is_int(j) || lra.column_has_term(j))
continue;
const impq& v = lra.get_column_value(j);
if (v.is_int())
continue;
flips.push_back({j, floor(v), false});
}
lra.round_to_integer_solution();
for (auto& f : flips)
f.m_at_hi = lra.get_column_value(f.m_j).x > f.m_lo;
if (repair_rounded_candidate(flips)) {
lra.set_status(lp_status::FEASIBLE);
SASSERT(lia.settings().get_cancel_flag() || lia.is_feasible());
TRACE(cube, tout << "largest cube success";);
lia.settings().stats().m_lcube_success++;
return lia_move::sat;
}
// return to the cube center: an interior point of the polyhedron
lra.restore_x();
lra.set_status(lp_status::FEASIBLE);
return lia_move::undef;
}
// Checks the rounded center against the original constraints. On failure
// searches the vertices of the lattice cell around the center greedily:
// flip a coordinate between floor and ceiling to maximally decrease the
// total bound violation, within a budget.
bool int_cube::repair_rounded_candidate(vector<flip_candidate>& flips) {
vector<bounded_row> rows;
for (const lar_term* t : lra.terms()) {
unsigned j = t->j();
if (!lra.column_associated_with_row(j))
continue;
if (!lra.column_has_upper_bound(j) && !lra.column_has_lower_bound(j))
continue;
bounded_row r;
r.m_j = j;
r.m_val = t->apply(lra.r_x());
rows.push_back(r);
}
auto row_violation = [&](unsigned ri, const impq& v) {
impq w;
unsigned j = rows[ri].m_j;
if (lra.column_has_upper_bound(j) && v > lra.get_upper_bound(j))
w += v - lra.get_upper_bound(j);
if (lra.column_has_lower_bound(j) && v < lra.get_lower_bound(j))
w += lra.get_lower_bound(j) - v;
return w;
};
impq violation;
for (unsigned ri = 0; ri < rows.size(); ++ri)
violation += row_violation(ri, rows[ri].m_val);
if (is_zero(violation))
return true; // the rounded center fits as it is
if (flips.empty())
return false;
std::unordered_map<unsigned, unsigned> flip_of_var;
for (unsigned fi = 0; fi < flips.size(); ++fi)
flip_of_var[flips[fi].m_j] = fi;
// occurrences of the flip candidates in the bounded rows
vector<vector<std::pair<unsigned, mpq>>> occs(flips.size());
for (unsigned ri = 0; ri < rows.size(); ++ri) {
const lar_term& t = lra.get_term(rows[ri].m_j);
for (lar_term::ival p : t) {
auto it = flip_of_var.find(p.j());
if (it != flip_of_var.end())
occs[it->second].push_back({ri, p.coeff()});
}
}
unsigned budget = std::min(2 * flips.size(), lia.settings().lcube_flips());
bool flipped = false;
while (!is_zero(violation) && budget-- > 0) {
unsigned best_fi = UINT_MAX;
impq best_gain;
for (unsigned fi = 0; fi < flips.size(); ++fi) {
if (occs[fi].empty())
continue;
mpq step = flips[fi].m_at_hi ? mpq(-1) : mpq(1);
impq gain;
for (const auto& o : occs[fi]) {
const impq& v = rows[o.first].m_val;
gain += row_violation(o.first, v + impq(step * o.second)) - row_violation(o.first, v);
}
if (gain < best_gain) {
best_gain = gain;
best_fi = fi;
}
}
if (seen_minus && seen_plus)
return zero_of_type<impq>();
return impq(0, 1);
if (best_fi == UINT_MAX)
return false; // no flip decreases the violation
mpq step = flips[best_fi].m_at_hi ? mpq(-1) : mpq(1);
for (const auto& o : occs[best_fi])
rows[o.first].m_val += impq(step * o.second);
flips[best_fi].m_at_hi = !flips[best_fi].m_at_hi;
violation += best_gain;
flipped = true;
TRACE(cube, tout << "flipped column " << flips[best_fi].m_j << ", violation = " << violation << "\n";);
}
if (!is_zero(violation))
return false;
// apply the repaired candidate
for (const auto& f : flips)
lra.set_column_value(f.m_j, impq(f.m_at_hi ? f.m_lo + 1 : f.m_lo));
for (const lar_term* t : lra.terms()) {
unsigned j = t->j();
if (!lra.column_associated_with_row(j))
continue;
lra.set_column_value(j, t->apply(lra.r_x()));
}
if (flipped)
lia.settings().stats().m_lcube_flip_success++;
return true;
}
impq int_cube::get_cube_delta_for_term(const lar_term& t) const {
if (t.size() == 2) {
bool seen_minus = false, seen_plus = false, all_ok = true;
for (lar_term::ival p : t) {
if (!lia.column_is_int(p.j())) { all_ok = false; break; }
const mpq& c = p.coeff();
if (c == one_of_type<mpq>()) seen_plus = true;
else if (c == -one_of_type<mpq>()) seen_minus = true;
else { all_ok = false; break; }
}
if (all_ok) {
if (seen_minus && seen_plus)
return zero_of_type<impq>();
return impq(0, 1);
}
}
usual_delta:
mpq delta = zero_of_type<mpq>();
for (lar_term::ival p : t)
if (lia.column_is_int(p.j()))
delta += abs(p.coeff());
delta *= mpq(1, 2);
return impq(delta);
}

View file

@ -10,9 +10,15 @@ Abstract:
Cube finder
This routine attempts to find a feasible integer solution
by tightnening bounds and running an LRA solver on the
by tightnening bounds and running an LRA solver on the
tighter system.
find_largest_cube() implements the largest cube test of
Bromberger and Weidenbach (Fast Cube Tests for LIA Constraint
Solving, IJCAR 2016): a fresh variable x_e for the cube edge
length is introduced and maximized; the center of the largest
cube is rounded to a candidate integer solution.
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
@ -21,7 +27,10 @@ Revision History:
--*/
#pragma once
#include "util/vector.h"
#include "math/lp/lia_move.h"
#include "math/lp/numeric_pair.h"
#include "math/lp/lar_term.h"
namespace lp {
class int_solver;
@ -29,12 +38,30 @@ namespace lp {
class int_cube {
class int_solver& lia;
class lar_solver& lra;
// a fractional integer coordinate of the cube center:
// the candidate value is m_lo or m_lo + 1
struct flip_candidate {
unsigned m_j = 0;
mpq m_lo;
bool m_at_hi = false;
};
// a term column with at least one bound, tracked during the repair
struct bounded_row {
unsigned m_j = 0;
impq m_val;
};
bool tighten_term_for_cube(unsigned i);
bool tighten_terms_for_cube();
void find_feasible_solution();
impq get_cube_delta_for_term(const lar_term& t) const;
bool add_edge_rows_for_term(unsigned i, unsigned x_e);
bool add_cube_edge_rows(unsigned x_e);
lia_move sat_after_rounding();
lia_move round_and_repair();
bool repair_rounded_candidate(vector<flip_candidate>& flips);
public:
int_cube(int_solver& lia);
lia_move operator()();
lia_move find_largest_cube();
};
}

View file

@ -44,7 +44,11 @@ namespace lp {
dioph_eq m_dio;
int_gcd_test m_gcd;
unsigned m_initial_dio_calls_period;
unsigned m_lcube_period;
// The number of consecutive genuine dio calls that returned undef, reset on a dio
// conflict. Drives the decision to start running Gomory with dio.
unsigned m_dio_undef_in_a_row = 0;
bool column_is_int_inf(unsigned j) const {
return lra.column_is_int(j) && (!lia.value_is_int(j));
}
@ -52,7 +56,8 @@ namespace lp {
imp(int_solver& lia): lia(lia), lra(lia.lra), lrac(lia.lrac), m_hnf_cutter(lia), m_dio(lia), m_gcd(lia) {
m_hnf_cut_period = settings().hnf_cut_period();
m_initial_dio_calls_period = settings().dio_calls_period();
}
m_lcube_period = settings().m_int_find_cube_period;
}
bool has_lower(unsigned j) const {
switch (lrac.m_column_types()[j]) {
@ -176,14 +181,16 @@ namespace lp {
if (r == lia_move::conflict) {
m_dio.explain(*this->m_ex);
lia.settings().dio_calls_period() = m_initial_dio_calls_period;
lia.settings().dio_enable_gomory_cuts() = false;
m_dio_undef_in_a_row = 0;
lia.settings().stop_running_gomory_with_dio(); // dio was productive: stop running Gomory
lia.settings().set_run_gcd_test(false);
return lia_move::conflict;
}
if (r == lia_move::undef) {
lia.settings().dio_calls_period() *= 2;
if (lra.settings().dio_calls_period() >= 16) {
lia.settings().dio_enable_gomory_cuts() = true;
lia.settings().dio_calls_period() *= 2; // throttle dio scheduling on failure
++m_dio_undef_in_a_row;
if (m_dio_undef_in_a_row >= lra.settings().dio_gomory_enable_period()) {
lia.settings().start_running_gomory_with_dio(); // dio persistently unproductive: start running Gomory
lia.settings().set_run_gcd_test(true);
}
}
@ -191,26 +198,66 @@ namespace lp {
}
lp_settings& settings() { return lra.settings(); }
// Decide whether a periodic heuristic fires on this call. When
// random_hammers is enabled the gate is drawn at random with the same
// 1/period expected rate instead of a deterministic "every k-th call"
// modulus: a deterministic period can phase-lock with the search on
// some families and drown the solver in conflicts while another handler
// is starved; randomizing the gate breaks that resonance.
bool hit_period(unsigned period) {
if (period <= 1)
return true;
if (settings().random_hammers())
return settings().random_next(period) == 0;
return m_number_of_calls % period == 0;
}
bool should_find_cube() {
return m_number_of_calls % settings().m_int_find_cube_period == 0;
return hit_period(settings().m_int_find_cube_period);
}
// The largest cube test is throttled exponentially: when the polyhedron
// does not contain a large enough cube it is unlikely to contain one
// later, after more constraints are added, so each failure doubles the
// period and a success resets it.
bool should_find_lcube() {
return settings().lcube() && hit_period(m_lcube_period);
}
lia_move find_lcube() {
lia_move r = int_cube(lia).find_largest_cube();
if (r == lia_move::undef) {
if (m_lcube_period < (1u << 30))
m_lcube_period *= 2;
}
else
m_lcube_period = settings().m_int_find_cube_period;
return r;
}
bool should_gomory_cut() {
bool dio_allows_gomory = !settings().dio() || settings().dio_enable_gomory_cuts() ||
m_dio.some_terms_are_ignored();
return dio_allows_gomory && m_number_of_calls % settings().m_int_gomory_cut_period == 0;
return dio_allows_gomory && hit_period(settings().m_int_gomory_cut_period);
}
bool should_solve_dioph_eq() {
return lia.settings().dio() && (m_number_of_calls % settings().dio_calls_period() == 0);
bool ret = lia.settings().dio() && hit_period(settings().dio_calls_period());
if (!ret && lia.settings().dio_calls_period() > m_initial_dio_calls_period) {
unsigned dec = settings().dio_calls_period_decrease();
unsigned& period = lia.settings().dio_calls_period();
period = period > m_initial_dio_calls_period + dec ? period - dec : m_initial_dio_calls_period;
}
return ret;
}
// HNF
bool should_hnf_cut() {
return (!settings().dio() || settings().dio_enable_hnf_cuts())
&& settings().enable_hnf() && m_number_of_calls % settings().hnf_cut_period() == 0;
&& settings().enable_hnf() && hit_period(settings().hnf_cut_period());
}
lia_move hnf_cut() {
@ -245,11 +292,12 @@ namespace lp {
++m_number_of_calls;
if (r == lia_move::undef) r = patch_basic_columns();
if (r == lia_move::undef && should_find_cube()) r = int_cube(lia)();
if (r == lia_move::undef && should_find_cube()) r = int_cube(lia)();
if (r == lia_move::undef && should_find_lcube()) r = find_lcube();
if (r == lia_move::undef) lra.move_non_basic_columns_to_bounds();
if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut();
if (r == lia_move::undef && should_gomory_cut()) r = gomory(lia).get_gomory_cuts(2);
if (r == lia_move::undef && should_solve_dioph_eq()) r = solve_dioph_eq();
if (r == lia_move::undef && should_gomory_cut()) r = gomory(lia).get_gomory_cuts(2);
if (r == lia_move::undef) r = int_branch(lia)();
if (settings().get_cancel_flag()) r = lia_move::undef;
return r;

View file

@ -864,7 +864,7 @@ namespace lp {
}
lp_status lar_solver::maximize_term(unsigned j,
impq& term_max) {
impq& term_max, bool fix_int_cols) {
TRACE(lar_solver, print_values(tout););
SASSERT(get_core_solver().m_r_solver.calc_current_x_is_feasible_include_non_basis());
lar_term term = get_term_to_maximize(j);
@ -879,6 +879,11 @@ namespace lp {
return lp_status::UNBOUNDED;
}
if (!fix_int_cols) {
set_status(lp_status::OPTIMAL);
return lp_status::OPTIMAL;
}
impq opt_val = term_max;
bool change = false;
@ -1120,8 +1125,10 @@ namespace lp {
void lar_solver::explain_fixed_column(unsigned j, explanation& ex) {
SASSERT(column_is_fixed(j));
auto* deps = get_bound_constraint_witnesses_for_column(j);
for (auto ci : flatten(deps))
const column& ul = m_imp->m_columns[j];
m_imp->m_tmp_dependencies.reset();
m_imp->m_dependencies.linearize(ul.lower_bound_witness(), ul.upper_bound_witness(), m_imp->m_tmp_dependencies);
for (auto ci : m_imp->m_tmp_dependencies)
ex.push_back(ci);
}

Some files were not shown because too many files have changed in this diff Show more