3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-14 11:05:41 +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 files: `.github/workflows/*.md` and `.github/workflows/**/*.md`
- Workflow lock files: `.github/workflows/*.lock.yml` - Workflow lock files: `.github/workflows/*.lock.yml`
- Shared components: `.github/workflows/shared/*.md` - 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 ## Problems This Solves
@ -54,10 +54,12 @@ When you interact with this agent, it will:
## Available Prompts ## 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 ### 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 **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**: **Use cases**:
- "Create a workflow that triages issues" - "Create a workflow that triages issues"
@ -67,7 +69,7 @@ When you interact with this agent, it will:
### Update Existing Workflow ### Update Existing Workflow
**Load when**: User wants to modify, improve, or refactor an 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**: **Use cases**:
- "Add web-fetch tool to the issue-classifier workflow" - "Add web-fetch tool to the issue-classifier workflow"
@ -77,7 +79,7 @@ When you interact with this agent, it will:
### Debug Workflow ### Debug Workflow
**Load when**: User needs to investigate, audit, debug, or understand a workflow, troubleshoot issues, analyze logs, or fix errors **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**: **Use cases**:
- "Why is this workflow failing?" - "Why is this workflow failing?"
@ -87,7 +89,7 @@ When you interact with this agent, it will:
### Upgrade Agentic Workflows ### Upgrade Agentic Workflows
**Load when**: User wants to upgrade workflows to a new gh-aw version or fix deprecations **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**: **Use cases**:
- "Upgrade all workflows to the latest version" - "Upgrade all workflows to the latest version"
@ -97,7 +99,7 @@ When you interact with this agent, it will:
### Create a Report-Generating Workflow ### 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 **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**: **Use cases**:
- "Create a weekly CI health report" - "Create a weekly CI health report"
@ -107,7 +109,7 @@ When you interact with this agent, it will:
### Create Shared Agentic Workflow ### Create Shared Agentic Workflow
**Load when**: User wants to create a reusable workflow component or wrap an MCP server **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**: **Use cases**:
- "Create a shared component for Notion integration" - "Create a shared component for Notion integration"
@ -117,7 +119,7 @@ When you interact with this agent, it will:
### Fix Dependabot PRs ### 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`) **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**: **Use cases**:
- "Fix the open Dependabot PRs for npm dependencies" - "Fix the open Dependabot PRs for npm dependencies"
@ -127,7 +129,7 @@ When you interact with this agent, it will:
### Analyze Test Coverage ### 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. **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**: **Use cases**:
- "Create a workflow that comments coverage on PRs" - "Create a workflow that comments coverage on PRs"
@ -137,7 +139,7 @@ When you interact with this agent, it will:
### CLI Commands Reference ### 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. **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**: **Use cases**:
- "How do I trigger workflow X on the main branch?" - "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 ### 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. **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**: **Use cases**:
- "How do I reduce the token cost of this workflow?" - "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 ### Workflow Pattern Selection
**Load when**: The user asks for architecture, strategy, operating model selection, or pattern recommendations for building agentic workflows. **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**: **Use cases**:
- "Which pattern should I use for multi-repo rollout?" - "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: When a user interacts with you:
1. **Identify the task type** from the user's request 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 3. **Follow the loaded prompt's instructions** exactly
4. **If uncertain**, ask clarifying questions to determine the right prompt 4. **If uncertain**, ask clarifying questions to determine the right prompt
@ -213,12 +215,12 @@ gh aw compile --validate
## Important Notes ## 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 - 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 - 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 - **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 - 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. - **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. - **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: updates:
- directory: / - directory: /
ignore: 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 package-ecosystem: github-actions
schedule: schedule:
interval: weekly interval: weekly

9
.github/mcp.json vendored
View file

@ -1,10 +1,19 @@
{ {
"mcpServers": { "mcpServers": {
"github-agentic-workflows": { "github-agentic-workflows": {
"type": "local",
"command": "gh", "command": "gh",
"args": [ "args": [
"aw", "aw",
"mcp-server" "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/memory.md`
- `.github/aw/messages.md` - `.github/aw/messages.md`
- `.github/aw/network.md` - `.github/aw/network.md`
- `.github/aw/optimize-agentic-workflow.md`
- `.github/aw/patterns.md` - `.github/aw/patterns.md`
- `.github/aw/pr-reviewer.md` - `.github/aw/pr-reviewer.md`
- `.github/aw/report.md` - `.github/aw/report.md`

View file

@ -28,7 +28,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Add msbuild to PATH - name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3 uses: microsoft/setup-msbuild@v3
- run: | - run: |
@ -37,9 +37,10 @@ jobs:
${{ matrix.cmd1 }} ${{ matrix.cmd1 }}
${{ matrix.cmd2 }} ${{ matrix.cmd2 }}
${{ matrix.cmd3 }} ${{ matrix.cmd3 }}
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"
cmake ${{ matrix.bindings }} -G "NMake Makefiles" ../ call "%VSPATH%\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.arch }} || exit /b 1
nmake cmake ${{ matrix.bindings }} -G "NMake Makefiles" ../ || exit /b 1
nmake || exit /b 1
cd .. cd ..
shell: cmd shell: cmd
- name: Run Regressions - name: Run Regressions
@ -52,7 +53,8 @@ jobs:
if: ${{ matrix.test }} if: ${{ matrix.test }}
run: | run: |
pushd build 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 pushd build\python
python z3test.py z3 python z3test.py z3
python z3test.py z3num python z3test.py z3num

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

@ -31,13 +31,13 @@
# - GITHUB_TOKEN # - GITHUB_TOKEN
# #
# Custom actions used: # Custom actions used:
# - actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@v9 # - actions/github-script@v9
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 # - 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: # Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6 # - ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6
@ -91,7 +91,7 @@ jobs:
steps: steps:
- name: Setup Scripts - name: Setup Scripts
id: setup id: setup
uses: github/gh-aw-actions/setup@v0.79.6 uses: github/gh-aw-actions/setup@v0.80.4
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }} job-name: ${{ github.job }}
@ -152,7 +152,7 @@ jobs:
env: env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Checkout .github and .agents folders - name: Checkout .github and .agents folders
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
sparse-checkout: | sparse-checkout: |
@ -377,7 +377,7 @@ jobs:
steps: steps:
- name: Setup Scripts - name: Setup Scripts
id: setup id: setup
uses: github/gh-aw-actions/setup@v0.79.6 uses: github/gh-aw-actions/setup@v0.80.4
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }} job-name: ${{ github.job }}
@ -398,7 +398,7 @@ jobs:
echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
} >> "$GITHUB_OUTPUT" } >> "$GITHUB_OUTPUT"
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
- name: Create gh-aw temp directory - name: Create gh-aw temp directory
@ -999,7 +999,7 @@ jobs:
steps: steps:
- name: Setup Scripts - name: Setup Scripts
id: setup id: setup
uses: github/gh-aw-actions/setup@v0.79.6 uses: github/gh-aw-actions/setup@v0.80.4
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }} job-name: ${{ github.job }}
@ -1194,7 +1194,7 @@ jobs:
steps: steps:
- name: Setup Scripts - name: Setup Scripts
id: setup id: setup
uses: github/gh-aw-actions/setup@v0.79.6 uses: github/gh-aw-actions/setup@v0.80.4
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }} job-name: ${{ github.job }}
@ -1222,7 +1222,7 @@ jobs:
echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- name: Checkout repository for patch context - name: Checkout repository for patch context
if: needs.agent.outputs.has_patch == 'true' if: needs.agent.outputs.has_patch == 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
# --- Threat Detection --- # --- Threat Detection ---
@ -1454,7 +1454,7 @@ jobs:
steps: steps:
- name: Setup Scripts - name: Setup Scripts
id: setup id: setup
uses: github/gh-aw-actions/setup@v0.79.6 uses: github/gh-aw-actions/setup@v0.80.4
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }} 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 /\__ \ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
# #
# This file was automatically generated by pkg/workflow/maintenance_workflow.go (v0.79.6). DO NOT EDIT.
# #
# To regenerate this workflow, run: # To regenerate this workflow, run:
# gh aw compile # gh aw compile
@ -20,15 +21,17 @@
# #
# For more information: https://github.github.com/gh-aw/introduction/overview/ # For more information: https://github.github.com/gh-aw/introduction/overview/
# #
# Alternative regeneration methods: # This file defines the generated agentic maintenance workflow for this repository.
# make recompile # It runs scheduled cleanup for expiring safe outputs and supports manual maintenance operations.
# #
# Or use the gh-aw CLI directly: # This workflow is generated automatically when workflows use expiring safe outputs
# ./gh-aw compile --validate --verbose # or when repository maintenance features are enabled in .github/workflows/aw.json.
# #
# The workflow is generated when any workflow uses the 'expires' field # To disable maintenance workflow generation, set in .github/workflows/aw.json:
# in create-discussions, create-issues, or create-pull-request safe-outputs configuration. # {"maintenance": false}
# Schedule frequency is automatically determined by the shortest expiration time. #
# Agentic maintenance docs:
# https://github.github.com/gh-aw/reference/ephemerals/#manual-maintenance-operations
# #
name: Agentic Maintenance name: Agentic Maintenance
@ -93,7 +96,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -131,7 +134,7 @@ jobs:
actions: write actions: write
steps: steps:
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -155,12 +158,12 @@ jobs:
operation: ${{ steps.record.outputs.operation }} operation: ${{ steps.record.outputs.operation }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -175,9 +178,9 @@ jobs:
await main(); await main();
- name: Install gh-aw - 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: with:
version: v0.79.6 version: v0.80.9
- name: Run operation - name: Run operation
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@ -195,7 +198,9 @@ jobs:
- name: Record outputs - name: Record outputs
id: record 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: 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)) }} 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 pull-requests: write
steps: steps:
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -244,14 +249,14 @@ jobs:
run_url: ${{ steps.record.outputs.run_url }} run_url: ${{ steps.record.outputs.run_url }}
steps: steps:
- name: Checkout actions folder - name: Checkout actions folder
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
sparse-checkout: | sparse-checkout: |
actions actions
persist-credentials: false persist-credentials: false
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -280,7 +285,9 @@ jobs:
- name: Record outputs - name: Record outputs
id: record 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: create_labels:
if: ${{ (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call') && inputs.operation == 'create_labels' && (!(github.event.repository.fork)) }} 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 issues: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -310,9 +317,9 @@ jobs:
await main(); await main();
- name: Install gh-aw - 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: with:
version: v0.79.6 version: v0.80.9
- name: Create missing labels - name: Create missing labels
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@ -336,12 +343,12 @@ jobs:
issues: write issues: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -356,13 +363,13 @@ jobs:
await main(); await main();
- name: Install gh-aw - 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: with:
version: v0.79.6 version: v0.80.9
- name: Restore activity report logs cache - name: Restore activity report logs cache
id: 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: with:
path: ./.cache/gh-aw/activity-report-logs path: ./.cache/gh-aw/activity-report-logs
key: ${{ runner.os }}-activity-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }} key: ${{ runner.os }}-activity-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}
@ -379,10 +386,10 @@ jobs:
${GH_AW_CMD_PREFIX} logs \ ${GH_AW_CMD_PREFIX} logs \
--repo "${{ github.repository }}" \ --repo "${{ github.repository }}" \
--start-date -1w \ --start-date -1w \
--count 100 \ --count 500 \
--output ./.cache/gh-aw/activity-report-logs \ --output ./.cache/gh-aw/activity-report-logs \
--format markdown \ --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 - name: Save activity report logs cache
if: ${{ always() }} if: ${{ always() }}
@ -441,12 +448,12 @@ jobs:
issues: write issues: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -461,13 +468,13 @@ jobs:
await main(); await main();
- name: Install gh-aw - 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: with:
version: v0.79.6 version: v0.80.9
- name: Restore forecast report logs cache - name: Restore forecast report logs cache
id: 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: with:
path: ./.github/aw/logs path: ./.github/aw/logs
key: ${{ runner.os }}-forecast-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }} key: ${{ runner.os }}-forecast-report-logs-${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}
@ -538,7 +545,7 @@ jobs:
issues: write issues: write
steps: steps:
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -570,12 +577,12 @@ jobs:
issues: write issues: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
- name: Setup Scripts - name: Setup Scripts
uses: github/gh-aw-actions/setup@5c2fe865bb4dc46e1450f6ee0d0541d759aea73a # v0.79.6 uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9
with: with:
destination: ${{ runner.temp }}/gh-aw/actions destination: ${{ runner.temp }}/gh-aw/actions
@ -590,9 +597,9 @@ jobs:
await main(); await main();
- name: Install gh-aw - 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: with:
version: v0.79.6 version: v0.80.9
- name: Validate workflows and file issue on findings - name: Validate workflows and file issue on findings
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

@ -38,7 +38,7 @@ jobs:
runRegressions: false runRegressions: false
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -81,7 +81,7 @@ jobs:
container: "quay.io/pypa/manylinux_2_34_x86_64:latest" container: "quay.io/pypa/manylinux_2_34_x86_64:latest"
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Select Python - name: Select Python
run: | run: |
@ -121,7 +121,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download ARM toolchain - 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' 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 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup OCaml - name: Setup OCaml
uses: ocaml/setup-ocaml@v3 uses: ocaml/setup-ocaml@v3
@ -220,7 +220,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup OCaml - name: Setup OCaml
uses: ocaml/setup-ocaml@v3 uses: ocaml/setup-ocaml@v3
@ -248,7 +248,19 @@ jobs:
cd .. cd ..
- name: Install Z3 OCaml package - 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 - name: Build and run OCaml examples
run: | run: |
@ -314,7 +326,7 @@ jobs:
runTests: false runTests: false
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -404,7 +416,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -453,7 +465,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -494,7 +506,7 @@ jobs:
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -514,7 +526,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

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

View file

@ -27,7 +27,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v5 uses: actions/setup-dotnet@v5
@ -87,7 +87,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v5 uses: actions/setup-dotnet@v5
@ -142,7 +142,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v5 uses: actions/setup-dotnet@v5
@ -197,7 +197,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v5 uses: actions/setup-dotnet@v5
@ -256,7 +256,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download Windows x64 build from release - name: Download Windows x64 build from release
env: env:
@ -292,7 +292,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download Windows x86 build from release - name: Download Windows x86 build from release
env: env:
@ -328,7 +328,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download Ubuntu x64 build from release - name: Download Ubuntu x64 build from release
env: env:
@ -361,7 +361,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS x64 build from release - name: Download macOS x64 build from release
env: env:
@ -394,7 +394,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 build from release - name: Download macOS ARM64 build from release
env: env:
@ -431,7 +431,7 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -470,7 +470,7 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -510,7 +510,7 @@ jobs:
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -553,7 +553,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -582,7 +582,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -611,7 +611,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -640,7 +640,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -672,7 +672,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -727,7 +727,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS x64 build from release - name: Download macOS x64 build from release
env: env:
@ -779,7 +779,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 build from release - name: Download macOS ARM64 build from release
env: env:
@ -835,7 +835,7 @@ jobs:
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -844,3 +844,97 @@ jobs:
- name: Run build script unit tests - name: Run build script unit tests
run: python -m unittest discover -s scripts/tests -p "test_*.py" -v 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 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -71,7 +71,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -112,7 +112,7 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS x64 Build - name: Download macOS x64 Build
uses: actions/download-artifact@v8.0.1 uses: actions/download-artifact@v8.0.1
@ -171,7 +171,7 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 Build - name: Download macOS ARM64 Build
uses: actions/download-artifact@v8.0.1 uses: actions/download-artifact@v8.0.1
@ -229,7 +229,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -258,7 +258,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -293,7 +293,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -349,7 +349,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Select Python - name: Select Python
run: | run: |
@ -387,7 +387,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download ARM toolchain - 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' 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 container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download RISC-V toolchain - 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' 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 path: src/api/python/wheelhouse/*.whl
retention-days: 2 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: windows-build-x64:
name: "Windows x64 build" name: "Windows x64 build"
runs-on: windows-latest runs-on: windows-latest
timeout-minutes: 120 timeout-minutes: 120
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -552,7 +499,8 @@ jobs:
- name: Build - name: Build
shell: cmd shell: cmd
run: | 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 python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact - name: Upload artifact
@ -568,7 +516,7 @@ jobs:
timeout-minutes: 120 timeout-minutes: 120
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -578,7 +526,8 @@ jobs:
- name: Build - name: Build
shell: cmd shell: cmd
run: | 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 python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact - name: Upload artifact
@ -594,7 +543,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -604,7 +553,8 @@ jobs:
- name: Build - name: Build
shell: cmd shell: cmd
run: | 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 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 - name: Upload artifact
@ -624,7 +574,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -699,7 +649,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -740,11 +690,11 @@ jobs:
python-package: python-package:
name: "Python packaging" 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 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -799,12 +749,6 @@ jobs:
name: ManyLinuxPythonBuildRiscv64 name: ManyLinuxPythonBuildRiscv64
path: artifacts path: artifacts
- name: Download Pyodide Build
uses: actions/download-artifact@v8.0.1
with:
name: PyodidePythonBuild
path: artifacts
- name: Extract builds - name: Extract builds
run: | run: |
cd artifacts cd artifacts
@ -865,7 +809,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download all artifacts - name: Download all artifacts
uses: actions/download-artifact@v8.0.1 uses: actions/download-artifact@v8.0.1

View file

@ -20,7 +20,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -30,7 +30,8 @@ jobs:
- name: Build Windows x64 - name: Build Windows x64
shell: cmd shell: cmd
run: | 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 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 - name: Upload Windows x64 artifact
@ -44,7 +45,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -54,7 +55,8 @@ jobs:
- name: Build Windows x86 - name: Build Windows x86
shell: cmd shell: cmd
run: | 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 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 - name: Upload Windows x86 artifact
@ -68,7 +70,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -78,7 +80,8 @@ jobs:
- name: Build Windows ARM64 - name: Build Windows ARM64
shell: cmd shell: cmd
run: | 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 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 - name: Upload Windows ARM64 artifact
@ -92,7 +95,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -113,7 +116,7 @@ jobs:
runs-on: macos-14 runs-on: macos-14
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -134,7 +137,7 @@ jobs:
runs-on: macos-14 runs-on: macos-14
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -157,7 +160,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -212,7 +215,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6

View file

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

View file

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

View file

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

View file

@ -36,7 +36,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -78,7 +78,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -122,7 +122,7 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS x64 Build - name: Download macOS x64 Build
uses: actions/download-artifact@v8.0.1 uses: actions/download-artifact@v8.0.1
@ -181,7 +181,7 @@ jobs:
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download macOS ARM64 Build - name: Download macOS ARM64 Build
uses: actions/download-artifact@v8.0.1 uses: actions/download-artifact@v8.0.1
@ -239,7 +239,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -268,7 +268,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -303,7 +303,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -359,7 +359,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Select Python - name: Select Python
run: | run: |
@ -397,7 +397,7 @@ jobs:
container: quay.io/pypa/manylinux_2_28_x86_64:latest container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download ARM toolchain - 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' 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 container: quay.io/pypa/manylinux_2_28_x86_64:latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download RISC-V toolchain - 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' 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 path: src/api/python/wheelhouse/*.whl
retention-days: 7 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: windows-build-x64:
name: "Windows x64 build" name: "Windows x64 build"
runs-on: windows-latest runs-on: windows-latest
timeout-minutes: 120 timeout-minutes: 120
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -562,7 +509,8 @@ jobs:
- name: Build - name: Build
shell: cmd shell: cmd
run: | 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 python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact - name: Upload artifact
@ -578,7 +526,7 @@ jobs:
timeout-minutes: 120 timeout-minutes: 120
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -588,7 +536,8 @@ jobs:
- name: Build - name: Build
shell: cmd shell: cmd
run: | 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 python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact - name: Upload artifact
@ -604,7 +553,7 @@ jobs:
timeout-minutes: 90 timeout-minutes: 90
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -614,7 +563,8 @@ jobs:
- name: Build - name: Build
shell: cmd shell: cmd
run: | 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 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 - name: Upload artifact
@ -634,7 +584,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -709,7 +659,7 @@ jobs:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -750,11 +700,11 @@ jobs:
python-package: python-package:
name: "Python packaging" 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 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@ -809,12 +759,6 @@ jobs:
name: ManyLinuxPythonBuildRiscv64 name: ManyLinuxPythonBuildRiscv64
path: artifacts path: artifacts
- name: Download Pyodide Build
uses: actions/download-artifact@v8.0.1
with:
name: PyodidePythonBuild
path: artifacts
- name: Extract builds - name: Extract builds
run: | run: |
cd artifacts cd artifacts
@ -873,7 +817,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download all artifacts - name: Download all artifacts
uses: actions/download-artifact@v8.0.1 uses: actions/download-artifact@v8.0.1
@ -929,7 +873,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v6.0.3 uses: actions/checkout@v7.0.0
- name: Download NuGet packages - name: Download NuGet packages
uses: actions/download-artifact@v8.0.1 uses: actions/download-artifact@v8.0.1

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

3
.gitignore vendored
View file

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

View file

@ -25,8 +25,17 @@ cmake(
lib_source = ":all_files", lib_source = ":all_files",
out_binaries = ["z3"], out_binaries = ["z3"],
out_shared_libs = select({ out_shared_libs = select({
"@platforms//os:linux": ["libz3.so"], # NOTE: These will need to be manually bumped along side the version in MODULE.bazel/VERSION.txt/CMake
# "@platforms//os:osx": ["libz3.dylib"], # FIXME: this is not working, libz3<version>.dylib is not copied "@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"], "@platforms//os:windows": ["libz3.dll"],
"//conditions:default": ["@platforms//:incompatible"], "//conditions:default": ["@platforms//:incompatible"],
}), }),

View file

@ -204,7 +204,12 @@ elseif (EMSCRIPTEN)
"-Os" "-Os"
"-s ALLOW_MEMORY_GROWTH=1" "-s ALLOW_MEMORY_GROWTH=1"
"-s ASSERTIONS=0" "-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" "-s ERROR_ON_UNDEFINED_SYMBOLS=1"
) )
endif() 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) | | [![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 ### 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 | 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) | [![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 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 ### Manual & Release Workflows
| Documentation | Release Build | WASM Release | NuGet Build | | 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) | | [![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 ### Agentic Workflows
| A3 Python | API Coherence | Code Simplifier | Release Notes | Workflow Suggestion | | API Coherence | Code Simplifier | Release Notes | Workflow Suggestion | Academic Citation |
| ----------|---------------|-----------------|---------------|---------------------| | -------------|-----------------|---------------|---------------------|-------------------|
| [![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 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 | | Issue Backlog | Memory Safety Report | QF-S Benchmark | Specbot Crash Analyzer | SMTLIB Benchmark Finder |
| ------------------|---------------------|------------------|------------|---------------| | --------------|----------------------|----------------|------------------------|-------------------------|
| [![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 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 | | TPTP Benchmark |
| ---------------------|-------------------|----------------|----------------------|--------------------| |----------------|
| [![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 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 [1]: #building-z3-on-windows-using-visual-studio-command-prompt
[2]: #building-z3-using-make-and-gccclang [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 ## Power Tools
* The [Axiom Profiler](https://github.com/viperproject/axiom-profiler-2) currently developed by ETH Zurich * 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 " set(ATOMIC_TEST_SOURCE "
#include <atomic> #include <atomic>
std::atomic<int> x; std::atomic<int> x;
@ -21,3 +26,4 @@ if (NOT BUILTIN_ATOMIC)
message(FATAL_ERROR "Host compiler must support std::atomic!") message(FATAL_ERROR "Host compiler must support std::atomic!")
endif() endif()
endif() endif()
endif()

View file

@ -20,6 +20,11 @@ set(CLANG_ONLY_WARNINGS
"-Wno-c++98-compat" "-Wno-c++98-compat"
"-Wno-c++98-compat-pedantic" "-Wno-c++98-compat-pedantic"
"-Wno-zero-length-array" "-Wno-zero-length-array"
"-Wc99-extensions"
"-Wsuggest-override"
"-Winconsistent-missing-override"
"-Wno-missing-field-initializers"
"-Wcast-qual"
) )
set(MSVC_WARNINGS "/W3") 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> <Description>Z3 is a satisfiability modulo theories solver from Microsoft Research.</Description>
<Copyright>Copyright Microsoft Corporation. All rights reserved.</Copyright> <Copyright>Copyright Microsoft Corporation. All rights reserved.</Copyright>
<PackageTags>smt constraint solver theorem prover</PackageTags> <PackageTags>smt constraint solver theorem prover</PackageTags>
<PlatformTarget>AnyCPU</PlatformTarget>
%s %s
</PropertyGroup> </PropertyGroup>

View file

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

View file

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

View file

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

View file

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

View file

@ -310,7 +310,7 @@ go run basic_example.go
## Memory Management ## 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 ## Thread Safety

View file

@ -89,6 +89,7 @@ type Context struct {
// NewContext creates a new Z3 context with default configuration. // NewContext creates a new Z3 context with default configuration.
func NewContext() *Context { func NewContext() *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(C.Z3_mk_config())} 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) { runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr) C.Z3_del_context(c.ptr)
}) })
@ -98,6 +99,7 @@ func NewContext() *Context {
// NewContextWithConfig creates a new Z3 context with the given configuration. // NewContextWithConfig creates a new Z3 context with the given configuration.
func NewContextWithConfig(cfg *Config) *Context { func NewContextWithConfig(cfg *Config) *Context {
ctx := &Context{ptr: C.Z3_mk_context_rc(cfg.ptr)} ctx := &Context{ptr: C.Z3_mk_context_rc(cfg.ptr)}
C.Z3_enable_concurrent_dec_ref(ctx.ptr)
runtime.SetFinalizer(ctx, func(c *Context) { runtime.SetFinalizer(ctx, func(c *Context) {
C.Z3_del_context(c.ptr) 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); 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); JavaInfo *info = static_cast<JavaInfo*>(_p);
ScopedCB scoped(info, cb); ScopedCB scoped(info, cb);
return info->jenv->CallBooleanMethod(info->jobj, info->on_binding, (jlong)_q, (jlong)_inst); return info->jenv->CallBooleanMethod(info->jobj, info->on_binding, (jlong)_q, (jlong)_inst);

View file

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

View file

@ -1,3 +1,29 @@
[build-system] [build-system]
requires = ["setuptools>=70"] requires = ["setuptools>=70"]
build-backend = "setuptools.build_meta" 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_PLATFORM = "emscripten"
BUILD_ARCH = "wasm32" BUILD_ARCH = "wasm32"
BUILD_OS_VERSION = os.environ['_PYTHON_HOST_PLATFORM'].split('_')[1:-1] BUILD_OS_VERSION = os.environ['_PYTHON_HOST_PLATFORM'].split('_')[1:-1]
build_env['CFLAGS'] = build_env.get('CFLAGS', '') + " -fexceptions" # Match Pyodide's native-wasm exception/longjmp ABI (see Makefile.envs in
build_env['CXXFLAGS'] = build_env.get('CXXFLAGS', '') + " -fexceptions" # Pyodide). The legacy JS-based "-fexceptions" makes libz3.so import
build_env['LDFLAGS'] = build_env.get('LDFLAGS', '') + " -fexceptions" # 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 IS_SINGLE_THREADED = True
ENABLE_LTO = False ENABLE_LTO = False
# build with pthread doesn't work. The WASM bindings are also single threaded. # 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): 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: 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 = '_'.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) 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 { Z3_symbol get_symbol(unsigned pos) const {
check_arg(pos, SYMBOL); 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 { 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; ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs); args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_eq(n1,n2)); args.push_back(mk_eq(n1,n2));
return mk_app(basic_family_id, PR_TRANSITIVITY_STAR, args.size(), args.data()); 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_num_args() == f2->get_num_args());
SASSERT(f1->get_decl() == f2->get_decl()); SASSERT(f1->get_decl() == f2->get_decl());
ptr_buffer<expr> args; 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)); args.push_back(mk_app(R, f1, f2));
proof* p = mk_app(basic_family_id, PR_MONOTONICITY, args.size(), args.data()); proof* p = mk_app(basic_family_id, PR_MONOTONICITY, args.size(), args.data());
return p; return p;
@ -2965,7 +2965,7 @@ proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, pr
if (proofs_disabled()) if (proofs_disabled())
return nullptr; return nullptr;
ptr_buffer<expr> args; ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs); args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_eq(s, t)); args.push_back(mk_eq(s, t));
return mk_app(basic_family_id, PR_REWRITE_STAR, args.size(), args.data()); 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) { 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"; CTRACE(mk_unit_resolution_bug, !is_or(f1), tout << mk_ll_pp(f1, *this) << "\n";
for (unsigned i = 1; i < num_proofs; ++i) for (unsigned i = 1; i < num_proofs; ++i)
tout << mk_pp(proofs[i], *this) << "\n"; 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";); tout << mk_pp(new_fact, *this) << "\n";);
ptr_buffer<expr> args; ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs); args.append(num_proofs, (expr* const *) proofs);
args.push_back(new_fact); args.push_back(new_fact);
#ifdef Z3DEBUG #ifdef Z3DEBUG
expr * f1 = get_fact(proofs[0]); 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()) if (proofs_disabled())
return nullptr; return nullptr;
ptr_buffer<expr> args; ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs); args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_oeq(n, def)); args.push_back(mk_oeq(n, def));
return mk_app(basic_family_id, PR_APPLY_DEF, args.size(), args.data()); 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; return nullptr;
check_nnf_proof_parents(num_proofs, proofs); check_nnf_proof_parents(num_proofs, proofs);
ptr_buffer<expr> args; ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs); args.append(num_proofs, (expr* const *) proofs);
args.push_back(mk_oeq(s, t)); args.push_back(mk_oeq(s, t));
return mk_app(basic_family_id, PR_NNF_POS, args.size(), args.data()); 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; return nullptr;
check_nnf_proof_parents(num_proofs, proofs); check_nnf_proof_parents(num_proofs, proofs);
ptr_buffer<expr> args; 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)); args.push_back(mk_oeq(mk_not(s), t));
return mk_app(basic_family_id, PR_NNF_NEG, args.size(), args.data()); 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) { proof * ast_manager::mk_clause_trail(unsigned n, expr* const* ps) {
ptr_buffer<expr> args; 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()); 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) for (unsigned i = 0; i < num_params; ++i)
parameters.push_back(params[i]); parameters.push_back(params[i]);
ptr_buffer<expr> args; ptr_buffer<expr> args;
args.append(num_proofs, (expr**) proofs); args.append(num_proofs, (expr* const *) proofs);
args.push_back(fact); args.push_back(fact);
return mk_app(basic_family_id, PR_TH_LEMMA, parameters.size(), parameters.data(), args.size(), args.data()); 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; } inline ast_manager & m() const { return m_manager; }
// label for an expression // label for an expression
std::string label_of_expr(const expr * e) const { std::string label_of_expr(const expr *e) const {
expr_ref er((expr*)e, m()); expr_ref er(const_cast<expr *>(e), m());
std::ostringstream out; std::ostringstream out;
out << er << std::flush; out << er << std::flush;
return escape_dot(out.str()); return escape_dot(out.str());

View file

@ -39,11 +39,16 @@ z3_add_component(rewriter
rewriter.cpp rewriter.cpp
seq_axioms.cpp seq_axioms.cpp
seq_eq_solver.cpp seq_eq_solver.cpp
seq_derive.cpp
seq_subset.cpp seq_subset.cpp
seq_split.cpp seq_split.cpp
seq_derive.cpp
seq_range_collapse.cpp
seq_range_predicate.cpp
seq_rewriter.cpp seq_rewriter.cpp
seq_regex_bisim.cpp seq_regex_bisim.cpp
seq_skolem.cpp seq_skolem.cpp
term_enumeration.cpp
th_rewriter.cpp th_rewriter.cpp
value_sweep.cpp value_sweep.cpp
var_subst.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> template<typename Cfg>
void bit_blaster_tpl<Cfg>::mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { 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()); expr_ref_vector out_bits(m());
out_bits.resize(sz);
for (unsigned i = 0; i < sz; ++i) { for (unsigned i = 0; i < sz; ++i) {
mk_iff(a_bits[i], b_bits[i], out); 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); 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()))) { for (expr *e : subterms::ground(expr_ref(r, m()))) {
if (m().is_ite(e, cond, r1, r2)) { if (m().is_ite(e, cond, r1, r2)) {
expr_safe_replace rep1(m()); m_rep1.reset();
expr_safe_replace rep2(m()); m_rep2.reset();
rep1.insert(e, r1); m_rep1.insert(e, r1);
rep2.insert(e, r2); m_rep2.insert(e, r2);
c = cond; c = cond;
th = r; th = r;
el = r; el = r;
rep1(th); m_rep1(th);
rep2(el); m_rep2(el);
return true; 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/ast.h"
#include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter.h"
#include "ast/rewriter/expr_safe_replace.h"
#include "util/params.h" #include "util/params.h"
/** /**
@ -64,6 +65,7 @@ class bool_rewriter {
ptr_vector<expr> m_todo1, m_todo2; ptr_vector<expr> m_todo1, m_todo2;
unsigned_vector m_counts1, m_counts2; unsigned_vector m_counts1, m_counts2;
expr_mark m_marked; 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_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); 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); expr_ref simplify_eq_ite(expr* value, expr* ite);
public: 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); updt_params(p);
} }
ast_manager & m() const { return m_manager; } ast_manager & m() const { return m_manager; }
@ -243,7 +245,9 @@ public:
void mk_nor(expr * arg1, expr * arg2, expr_ref & result); void mk_nor(expr * arg1, expr * arg2, expr_ref & result);
void mk_ge2(expr* a, expr* b, expr* c, 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); 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 // |t| = 0 => |s| = 0 or indexof(t,s,offset) = -1
// ~contains(t,s) => 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(cnt, i_eq_m1);
add_clause(~t_eq_empty, s_eq_empty, 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, i_ge_len_s, mk_eq(i, len_x));
} }
add_clause(i_ge_0, mk_seq_eq(e, emp)); add_clause(i_ge_0, mk_eq(e, emp));
add_clause(~i_ge_len_s, mk_seq_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(~i_ge_0, i_ge_len_s, mk_eq(one, len_e));
add_clause(mk_le(len_e, 1)); add_clause(mk_le(len_e, 1));
} }
@ -1066,7 +1068,7 @@ namespace seq {
void axioms::replace_re_axiom(expr* e) { void axioms::replace_re_axiom(expr* e) {
expr* s = nullptr, *r = nullptr, *t = nullptr; expr* s = nullptr, *r = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_re(e, s, r, t)); 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 // A basic strategy for supporting replace_all and other
@ -1075,34 +1077,22 @@ namespace seq {
// using iterative deepening can be re-used. // using iterative deepening can be re-used.
// //
// create recursive relation 'ra' with properties: // create recursive relation 'ra' with properties:
// ra(i, j, s, p, t, r) = // ra(i, j, s, p, t, r) <- len(s) = i && len(r) = j
// if len(s) = i && len(r) = j then // ra(i, j, s, p, t, r) <- len(s) > i = 0 && p = "" && r = t + s
// true // 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)
// else if len(s) > i = 0 && p = "" && r = t + s then // 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)
// true // which amounts to:
// 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
// Then assert // Then assert
// ra(s, p, t, replace_all(s, p, t)) // 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) { void axioms::replace_all_axiom(expr* r) {
expr* s = nullptr, *p = nullptr, *t = nullptr; expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_all(r, s, p, t)); VERIFY(seq.str.is_replace_all(r, s, p, t));
recfun::util rec(m); recfun::util rec(m);
recfun::decl::plugin& plugin = rec.get_plugin(); recfun::decl::plugin& plugin = rec.get_plugin();
recfun_replace replace(m);
sort* srt = s->get_sort(); sort* srt = s->get_sort();
sort* domain[4] = { srt, srt, srt, srt }; sort* domain[4] = { srt, srt, srt, srt };
auto ra = rec.find_def_decl(symbol("ra"), 4, domain, m.mk_bool_sort(), true); 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) { void axioms::replace_re_all_axiom(expr* e) {
expr* s = nullptr, *p = nullptr, *t = nullptr; expr* s = nullptr, *p = nullptr, *t = nullptr;
VERIFY(seq.str.is_replace_re_all(e, s, p, t)); 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");
} }

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); 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. Fast inequivalence check based on the get_info().classical flag.
@ -228,15 +193,19 @@ namespace seq {
m_worklist.pop_back(); m_worklist.pop_back();
// Compute the symbolic derivative wrt the canonical variable // Compute the symbolic derivative wrt the canonical variable
// (:var 0). The result is a transition regex (ITE tree) whose // (:var 0) and enumerate its reachable leaves in fully
// leaves are regex expressions. We use the classical Brzozowski // ITE-hoisted normal form. Every if-then-else over the input
// entry point so the derivative stays as a single TRegex and // character — even one that would otherwise be buried under a
// does not lift unions to the top via antimirov nodes — this // concat or union — is hoisted to the top and infeasible
// preserves the XOR-pair invariant the bisimulation relies on. // minterms are pruned, so each leaf is a ground regex free of
expr_ref d(m_rw.mk_brz_derivative(r), m); // (: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); expr_ref_vector leaves(m);
if (!collect_leaves(d, leaves)) for (auto const& p : cofs)
return l_undef; leaves.push_back(p.second);
// First pass: check for any nullable leaf (definitive // First pass: check for any nullable leaf (definitive
// distinguishing empty-continuation word) or any classically // distinguishing empty-continuation word) or any classically

View file

@ -74,7 +74,6 @@ namespace seq {
unsigned node_of(expr* r); unsigned node_of(expr* r);
bool merge_leaf(expr* xor_pair); bool merge_leaf(expr* xor_pair);
bool collect_leaves(expr* der, expr_ref_vector& leaves);
lbool nullability(expr* r); lbool nullability(expr* r);
bool is_supported(expr* r); bool is_supported(expr* r);
// Returns true if the leaf l proves that the original pair is // 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 #pragma once
#include "seq_split.h"
#include "ast/seq_decl_plugin.h" #include "ast/seq_decl_plugin.h"
#include "ast/rewriter/seq_derive.h"
#include "ast/ast_pp.h" #include "ast/ast_pp.h"
#include "ast/arith_decl_plugin.h" #include "ast/arith_decl_plugin.h"
#include "ast/rewriter/rewriter_types.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); void insert(decl_kind op, expr* a, expr* b, expr* c, expr* r);
}; };
friend class seq::derive;
seq_util m_util; seq_util m_util;
seq_subset m_subset; seq_subset m_subset;
seq_split m_split; seq_split m_split;
arith_util m_autil; arith_util m_autil;
bool_rewriter m_br; bool_rewriter m_br;
seq::derive m_derive;
// re2automaton m_re2aut; // re2automaton m_re2aut;
op_cache m_op_cache; op_cache m_op_cache;
expr_ref_vector m_es, m_lhs, m_rhs; expr_ref_vector m_es, m_lhs, m_rhs;
bool m_coalesce_chars; bool m_coalesce_chars = true;
bool m_in_bisim { false }; bool m_in_bisim { false };
unsigned m_re_deriv_depth { 0 };
static const unsigned m_max_re_deriv_depth = 512;
enum length_comparison { enum length_comparison {
shorter_c, shorter_c,
@ -175,50 +182,22 @@ class seq_rewriter {
//replace b in a by c into result //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); 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 // For replace_all(x, a, b) in R: transform R so that
expr_ref is_nullable_rec(expr* r); // - occurrences of b_ch are replaced by union(to_re(a_str), to_re(b_str))
expr_ref mk_derivative_rec(expr* ele, expr* r); // - occurrences of a_ch are replaced by empty (replace_all never outputs a)
expr_ref mk_der_op(decl_kind k, expr* a, expr* b); expr_ref re_replace_char(expr *r, unsigned a_ch, unsigned b_ch, expr *a_str, expr *b_str);
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
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); 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 // 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 // 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 lt_char(expr* ch1, expr* ch2);
bool eq_char(expr* ch1, expr* ch2); bool eq_char(expr* ch1, expr* ch2);
bool neq_char(expr* ch1, expr* ch2); bool neq_char(expr* ch1, expr* ch2);
bool le_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 are_complements(expr* r1, expr* r2) const;
bool is_subset(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_union0(expr* a, expr* b, expr_ref& result);
br_status mk_re_inter0(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); 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_star(expr* a, expr_ref& result);
br_status mk_re_diff(expr* a, expr* b, 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); 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); lbool some_string_in_re(expr_mark& visited, expr* r, unsigned_vector& str);
public: public:
seq_rewriter(ast_manager & m, params_ref const & p = params_ref()): 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), 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_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(); } ast_manager & m() const { return m_util.get_manager(); }
family_id get_fid() const { return m_util.get_family_id(); } family_id get_fid() const { return m_util.get_family_id(); }
@ -361,7 +348,7 @@ public:
static void get_param_descrs(param_descrs & r); 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_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); br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
@ -377,6 +364,34 @@ public:
return result; 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 * makes concat and simplifies
*/ */
@ -460,11 +475,35 @@ public:
variable v0 = (:var 0). Unlike `mk_derivative` this entry point keeps variable v0 = (:var 0). Unlike `mk_derivative` this entry point keeps
the symbolic derivative as a single transition regex (TRegex): boolean the symbolic derivative as a single transition regex (TRegex): boolean
operators are pushed into the ITE leaves rather than lifted to the top 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 procedure which relies on each leaf of D(p XOR q) being a coherent
XOR pair (D_v p) XOR (D_v q). 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. // heuristic elimination of element from condition that comes form a derivative.
// special case optimization for conjunctions of equalities, disequalities and ranges. // 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)*/ /* 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); expr_ref mk_regex_inter_normalize(expr* r1, expr* r2);
/* expr_ref mk_regex_concat(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);
/* /*
* Extract some string that is a member of r. * Extract some string that is a member of r.

View file

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

View file

@ -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) if (m_re.is_dot_plus(b) && m_re.get_info(a).nullable == l_false)
return true; return true;
// a ⊆ a*
if (m_re.is_star(b, b1) && is_subset_rec(a, b1, depth))
return true;
// e ⊆ a* // e ⊆ a*
if (m_re.is_epsilon(a) && m_re.is_star(b, b1)) if (m_re.is_epsilon(a) && m_re.is_star(b, b1))
return true; return true;
// R ⊆ R* // a ⊆ a*: if b = b1* and a ⊆ b1, then a ⊆ b1*
if (m_re.is_star(b, b1) && is_subset_rec(a, b1, depth + 1)) if (m_re.is_star(b, b1) && is_subset_rec(a, b1, depth))
return true; return true;
// R1* ⊆ R2* if R1 ⊆ R2 // 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)) if (m_re.is_concat(b, b1, b2) && m_re.is_full_seq(b1) && is_subset_rec(a, b2, depth))
return true; 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' // 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)) if (m_re.is_concat(b, b1, b2) && m_re.is_full_seq(b2) && is_subset_rec(a, b1, depth))
return true; 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 { bool seq_subset::is_subset(expr* a, expr* b) const {
return is_subset_rec(a, b, 0); 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; 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: public:
explicit seq_subset(seq_util::rex& re) : m_re(re) {} explicit seq_subset(seq_util::rex& re) : m_re(re) {}
bool is_subset(expr* a, expr* b) const; 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_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_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_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_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_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); 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_COMPLEMENT:
case OP_RE_REVERSE: case OP_RE_REVERSE:
case OP_RE_DERIVATIVE: case OP_RE_DERIVATIVE:
case _OP_RE_ANTIMIROV_UNION:
m_has_re = true; m_has_re = true;
Z3_fallthrough; Z3_fallthrough;
case OP_SEQ_UNIT: case OP_SEQ_UNIT:
@ -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); 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 { bool seq_util::rex::is_loop(expr const* n, expr*& body, unsigned& lo, unsigned& hi) const {
if (is_loop(n)) { if (is_loop(n)) {
app const* a = to_app(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, r1);
print(out, r2); 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 << "("; out << "(";
print(out, r1); print(out, r1);
out << (html_encode ? "&#x22C3;" : "|"); 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: case OP_RE_OPTION:
i1 = get_info_rec(e->get_arg(0)); i1 = get_info_rec(e->get_arg(0));
return i1.opt(); 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_FULL_CHAR_SET:
case OP_RE_OF_PRED: case OP_RE_OF_PRED:
//TBD: check if the character predicate contains uninterpreted symbols or is nonground or is unsat //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); return info(true, l_false, 1, false);
case OP_RE_CONCAT: case OP_RE_CONCAT:
i1 = get_info_rec(e->get_arg(0)); i1 = get_info_rec(e->get_arg(0));

View file

@ -109,7 +109,6 @@ enum seq_op_kind {
_OP_REGEXP_EMPTY, _OP_REGEXP_EMPTY,
_OP_REGEXP_FULL_CHAR, _OP_REGEXP_FULL_CHAR,
_OP_RE_IS_NULLABLE, _OP_RE_IS_NULLABLE,
_OP_RE_ANTIMIROV_UNION, // Lifted union for antimirov-style derivatives
_OP_SEQ_SKOLEM, _OP_SEQ_SKOLEM,
LAST_SEQ_OP 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_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_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); } 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_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_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); } 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_of_pred(expr* p);
app* mk_reverse(expr* r) { return m.mk_app(m_fid, OP_RE_REVERSE, r); } 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_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_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); } 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_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_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_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_UNARY(is_to_re);
MATCH_BINARY(is_concat); MATCH_BINARY(is_concat);
MATCH_BINARY(is_union); MATCH_BINARY(is_union);
@ -595,7 +594,6 @@ public:
MATCH_UNARY(is_of_pred); MATCH_UNARY(is_of_pred);
MATCH_UNARY(is_reverse); MATCH_UNARY(is_reverse);
MATCH_BINARY(is_derivative); 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, unsigned& hi) const;
bool is_loop(expr const* n, expr*& body, unsigned& lo) 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; bool is_loop(expr const* n, expr*& body, expr*& lo, expr*& hi) const;

View file

@ -45,7 +45,7 @@ Author:
class dependent_expr_state { class dependent_expr_state {
unsigned m_qhead = 0; unsigned m_qhead = 0;
bool m_suffix_frozen = false; 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; lbool m_has_quantifiers = l_undef;
ast_mark m_frozen; ast_mark m_frozen;
func_decl_ref_vector m_frozen_trail; 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 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 analysis between each elimination. We address this by using reference counts and maintaining
a heap of reference counts. 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. as those introduced for bit-vectors use side constraints.
- it is not modular: we detach the expression invertion routines to self-contained code. - 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: 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. FV is built from initial terms.
2. maintain parent: term -> term-list of parent occurrences. 2. maintain parent: term -> term-list of parent occurrences.
3. repeat 3. repeat

View file

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

View file

@ -7,7 +7,6 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector>
#include "ast/arith_decl_plugin.h" #include "ast/arith_decl_plugin.h"
#include "ast/array_decl_plugin.h" #include "ast/array_decl_plugin.h"
@ -65,7 +64,8 @@ enum class token_kind {
slash_tok, slash_tok,
minus_tok, minus_tok,
at_tok, at_tok,
lambda_tok lambda_tok,
modal_tok
}; };
struct parse_error : public std::exception { struct parse_error : public std::exception {
@ -87,6 +87,7 @@ struct token {
std::string text; std::string text;
unsigned line = 1; unsigned line = 1;
unsigned col = 1; unsigned col = 1;
bool dquote = false; // true for double-quoted strings ("..."): TPTP distinct objects
}; };
class lexer { class lexer {
@ -163,6 +164,7 @@ public:
if (peek() == '\'' || peek() == '"') { if (peek() == '\'' || peek() == '"') {
char q = get(); char q = get();
t.kind = token_kind::str; t.kind = token_kind::str;
t.dquote = (q == '"');
while (!eof()) { while (!eof()) {
char c = get(); char c = get();
if (c == '\\' && !eof()) { if (c == '\\' && !eof()) {
@ -252,7 +254,7 @@ public:
case '^': t.kind = token_kind::lambda_tok; return t; case '^': t.kind = token_kind::lambda_tok; return t;
case '{': case '{':
// Modal operators: {$box}, {$dia}, etc. — lex as identifier including braces // 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); t.text.push_back(c);
while (!eof() && peek() != '}') while (!eof() && peek() != '}')
t.text.push_back(get()); t.text.push_back(get());
@ -277,10 +279,10 @@ public:
}; };
struct parsed_type { struct parsed_type {
std::vector<sort*> domain; ptr_vector<sort> domain;
sort* range = nullptr; sort* range = nullptr;
parsed_type(sort* s): range(s) {} 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 { class tptp_parser {
@ -290,13 +292,20 @@ class tptp_parser {
array_util m_array; array_util m_array;
sort* m_univ; sort* m_univ;
bool m_has_conjecture = false; 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_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; std::unordered_map<std::string, sort*> m_sorts;
sort_ref_vector m_pinned_sorts; // prevents cached sorts from being freed sort_ref_vector m_pinned_sorts; // prevents cached sorts from being freed
std::unordered_map<std::string, func_decl*> m_decls; std::unordered_map<std::string, func_decl*> m_decls;
func_decl_ref_vector m_pinned_decls; // prevents cached func_decls from being freed 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 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; 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 @) bool m_in_at_arg = false; // true when parsing inside @ argument (lambda body stops consuming @)
struct implicit_var_scope { struct implicit_var_scope {
@ -424,6 +433,7 @@ class tptp_parser {
std::string parse_name() { std::string parse_name() {
if (is(token_kind::id) || is(token_kind::str)) { if (is(token_kind::id) || is(token_kind::str)) {
m_last_name_quoted = 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; std::string r = m_curr.text;
next(); next();
return r; return r;
@ -449,7 +459,7 @@ class tptp_parser {
// For higher-order types like ($i > $o), create an uninterpreted sort // For higher-order types like ($i > $o), create an uninterpreted sort
// Function type A > B is represented as Array(A, B). // Function type A > B is represented as Array(A, B).
// Multi-argument A * B > C is represented as Array(A, Array(B, C)) (curried). // 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; sort* s = range;
for (int i = (int)domain.size() - 1; i >= 0; --i) for (int i = (int)domain.size() - 1; i >= 0; --i)
s = m_array.mk_array_sort(domain[i], s); s = m_array.mk_array_sort(domain[i], s);
@ -513,7 +523,8 @@ class tptp_parser {
if (itt != m_typed_decls.end()) { if (itt != m_typed_decls.end()) {
std::string typed_decl_key = mk_decl_key(name, arity, 'd'); std::string typed_decl_key = mk_decl_key(name, arity, 'd');
auto itd = m_decls.find(typed_decl_key); 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; auto const& sig = itt->second;
func_decl* f = m.mk_func_decl(symbol(name), sig.first.size(), sig.first.data(), sig.second); func_decl* f = m.mk_func_decl(symbol(name), sig.first.size(), sig.first.data(), sig.second);
m_pinned_decls.push_back(f); m_pinned_decls.push_back(f);
@ -525,7 +536,7 @@ class tptp_parser {
auto itd = m_decls.find(key); auto itd = m_decls.find(key);
if (itd != m_decls.end()) return itd->second; 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); 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_pinned_decls.push_back(f);
m_decls.emplace(key, f); m_decls.emplace(key, f);
@ -571,6 +582,14 @@ class tptp_parser {
expr_ref coerce_arg(expr_ref const& e, sort* target) { expr_ref coerce_arg(expr_ref const& e, sort* target) {
sort* actual = e->get_sort(); sort* actual = e->get_sort();
if (actual == target) return e; 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 // 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 box_name = std::string("$box_") + actual->get_name().str() + "_to_" + target->get_name().str();
std::string key = mk_decl_key(box_name, 1, 'f'); 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 // <defined_type> ::= $oType | $o | $iType | $i | $tType | $real | $rat | $int
parsed_type parse_type_atom() { parsed_type parse_type_atom() {
if (accept(token_kind::lparen)) { 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)) { if (accept(token_kind::gt_tok)) {
// Full function type inside parens: (A * B > C) or (A > B > C) // Full function type inside parens: (A * B > C) or (A > B > C)
parsed_type rhs = parse_type_expr(); parsed_type rhs = parse_type_expr();
std::vector<sort*> full_domain = prod; ptr_vector<sort> full_domain = prod;
if (!rhs.domain.empty()) { if (!rhs.domain.empty()) {
// Nested higher-order: (A > B > C) → flatten // 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, "')'"); expect(token_kind::rparen, "')'");
// Return with domain/range preserved for proper flattening // 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> // Grammar: <thf_xprod_type> ::= <thf_unitary_type> * <thf_unitary_type>
// | <thf_xprod_type> * <thf_unitary_type> // | <thf_xprod_type> * <thf_unitary_type>
// Product types form the domain in mapping types: (A * B) > C // 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(); parsed_type first = parse_type_atom();
if (!first.domain.empty() && first.range == nullptr) { if (!first.domain.empty() && first.range == nullptr) {
// Already a parenthesized product from nested parens // 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)) { while (accept(token_kind::star_tok)) {
parsed_type t = parse_type_atom(); parsed_type t = parse_type_atom();
if (!t.domain.empty()) { if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end()); args.append(t.domain);
} else { } else {
args.push_back(t.range); args.push_back(t.range);
} }
@ -761,28 +780,28 @@ class tptp_parser {
if (!first.domain.empty()) { if (!first.domain.empty()) {
// Function type as first element of product — use ho_sort // Function type as first element of product — use ho_sort
sort* ho = get_ho_sort(first.domain, first.range); sort* ho = get_ho_sort(first.domain, first.range);
std::vector<sort*> args; ptr_vector<sort> args;
args.push_back(ho); args.push_back(ho);
while (accept(token_kind::star_tok)) { while (accept(token_kind::star_tok)) {
parsed_type t = parse_type_atom(); parsed_type t = parse_type_atom();
if (!t.domain.empty() && t.range != nullptr) { if (!t.domain.empty() && t.range != nullptr) {
args.push_back(get_ho_sort(t.domain, t.range)); args.push_back(get_ho_sort(t.domain, t.range));
} else if (!t.domain.empty()) { } else if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end()); args.append(t.domain);
} else { } else {
args.push_back(t.range); args.push_back(t.range);
} }
} }
return args; return args;
} }
std::vector<sort*> args; ptr_vector<sort> args;
args.push_back(first.range); args.push_back(first.range);
while (accept(token_kind::star_tok)) { while (accept(token_kind::star_tok)) {
parsed_type t = parse_type_atom(); parsed_type t = parse_type_atom();
if (!t.domain.empty() && t.range != nullptr) { if (!t.domain.empty() && t.range != nullptr) {
args.push_back(get_ho_sort(t.domain, t.range)); args.push_back(get_ho_sort(t.domain, t.range));
} else if (!t.domain.empty()) { } else if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end()); args.append(t.domain);
} else { } else {
args.push_back(t.range); args.push_back(t.range);
} }
@ -801,7 +820,7 @@ class tptp_parser {
return first; return first;
} }
// Build product vector // Build product vector
std::vector<sort*> args; ptr_vector<sort> args;
if (!first.domain.empty() && first.range != nullptr) { if (!first.domain.empty() && first.range != nullptr) {
// Function type used as element in a product // Function type used as element in a product
args.push_back(get_ho_sort(first.domain, first.range)); args.push_back(get_ho_sort(first.domain, first.range));
@ -816,7 +835,7 @@ class tptp_parser {
if (!t.domain.empty() && t.range != nullptr) { if (!t.domain.empty() && t.range != nullptr) {
args.push_back(get_ho_sort(t.domain, t.range)); args.push_back(get_ho_sort(t.domain, t.range));
} else if (!t.domain.empty()) { } else if (!t.domain.empty()) {
args.insert(args.end(), t.domain.begin(), t.domain.end()); args.append(t.domain);
} else { } else {
args.push_back(t.range); 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)) { if (is(token_kind::type_forall_tok) || is(token_kind::type_exists_tok)) {
next(); next();
expect(token_kind::lbrack, "'['"); expect(token_kind::lbrack, "'['");
std::vector<sort*> type_params; ptr_vector<sort> type_params;
if (!accept(token_kind::rbrack)) { if (!accept(token_kind::rbrack)) {
do { do {
std::string tv = parse_name(); std::string tv = parse_name();
@ -848,8 +867,8 @@ class tptp_parser {
parsed_type inner = parse_type_expr(); parsed_type inner = parse_type_expr();
// Prepend type params to domain // Prepend type params to domain
if (!type_params.empty()) { if (!type_params.empty()) {
std::vector<sort*> full_domain = type_params; ptr_vector<sort> full_domain = type_params;
full_domain.insert(full_domain.end(), inner.domain.begin(), inner.domain.end()); full_domain.append(inner.domain);
return parsed_type(full_domain, inner.range); return parsed_type(full_domain, inner.range);
} }
return inner; return inner;
@ -858,7 +877,7 @@ class tptp_parser {
if (accept(token_kind::gt_tok)) { if (accept(token_kind::gt_tok)) {
parsed_type rhs = parse_type_expr(); parsed_type rhs = parse_type_expr();
// prod is either a product (domain non-empty, range==nullptr) or a single sort (domain empty) // 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) { if (!prod.domain.empty() && prod.range == nullptr) {
domain = prod.domain; domain = prod.domain;
} else if (!prod.domain.empty() && prod.range != nullptr) { } else if (!prod.domain.empty() && prod.range != nullptr) {
@ -869,7 +888,7 @@ class tptp_parser {
} }
if (!rhs.domain.empty()) { if (!rhs.domain.empty()) {
// Higher-order result type: A > (B > C) flattened to (A, B) > C // 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);
} }
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) // Grammar: (same as parse_term, primary productions)
expr_ref parse_term_primary() { expr_ref parse_term_primary() {
if (accept(token_kind::lparen)) { if (accept(token_kind::lparen)) {
expr_ref e = parse_formula(); expr_ref e = parse_formula(false);
expect(token_kind::rparen, "')'"); expect(token_kind::rparen, "')'");
return e; return e;
} }
@ -946,6 +965,7 @@ class tptp_parser {
return parse_numeral_from_name(n); return parse_numeral_from_name(n);
} }
bool dq_name = m_last_name_dquoted;
expr_ref b(m); expr_ref b(m);
// Check bound variables: uppercase (quantifier vars) AND lowercase (let-bound names) // Check bound variables: uppercase (quantifier vars) AND lowercase (let-bound names)
if (!m_last_name_quoted && find_bound(n, b)) { 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) // $ite needs special parsing: first arg is formula, rest are formulas (branches can be equalities)
if (n == "$ite") { if (n == "$ite") {
expect(token_kind::lparen, "'('"); expect(token_kind::lparen, "'('");
args.push_back(parse_formula()); args.push_back(parse_formula(true));
expect(token_kind::comma, "','"); expect(token_kind::comma, "','");
args.push_back(parse_formula()); args.push_back(parse_formula(false));
expect(token_kind::comma, "','"); expect(token_kind::comma, "','");
args.push_back(parse_formula()); args.push_back(parse_formula(false));
expect(token_kind::rparen, "')'"); expect(token_kind::rparen, "')'");
} }
else if (n == "$let") { else if (n == "$let") {
@ -995,14 +1015,17 @@ class tptp_parser {
} }
func_decl* f = mk_decl_or_ho_const(n, args.size(), false); func_decl* f = mk_decl_or_ho_const(n, args.size(), false);
if (!args.empty()) coerce_args(f, args); coerce_args(f, args);
return expr_ref(args.empty() ? m.mk_const(f) : m.mk_app(f, args.size(), args.data()), m); 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> // Grammar: <tff_logic_formula> ::= <tff_unitary_formula> | <tff_binary_formula>
// <thf_logic_formula> ::= <thf_unitary_formula> | <thf_binary_formula> // <thf_logic_formula> ::= <thf_unitary_formula> | <thf_binary_formula>
// Entry point for formula parsing (wraps parse_expr with default precedence). // 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> // Grammar: <thf_apply_formula> ::= <thf_unitary_formula> @ <thf_unitary_formula>
// | <thf_apply_formula> @ <thf_unitary_formula> // | <thf_apply_formula> @ <thf_unitary_formula>
@ -1042,7 +1065,7 @@ class tptp_parser {
return parse_lambda_expr(); return parse_lambda_expr();
} }
if (accept(token_kind::lparen)) { if (accept(token_kind::lparen)) {
expr_ref e = parse_formula(); expr_ref e = parse_formula(false);
expect(token_kind::rparen, "')'"); expect(token_kind::rparen, "')'");
// Do NOT call apply_at here — outer apply_at owns the remaining @ tokens // Do NOT call apply_at here — outer apply_at owns the remaining @ tokens
return e; return e;
@ -1074,7 +1097,7 @@ class tptp_parser {
// Quantifier body in @-arg should NOT consume @ — those belong to enclosing application // Quantifier body in @-arg should NOT consume @ — those belong to enclosing application
bool save_in_at_arg = m_in_at_arg; bool save_in_at_arg = m_in_at_arg;
m_in_at_arg = true; 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_in_at_arg = save_in_at_arg;
m_bound.pop_back(); m_bound.pop_back();
return mk_quantifier(is_forall, vars, body); 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()); std::string key = mk_decl_key(name_str, 0, 'c') + "\x1f" + std::to_string(range->get_id());
auto it = m_decls.find(key); auto it = m_decls.find(key);
if (it != m_decls.end()) return it->second; 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_pinned_decls.push_back(f);
m_decls.emplace(key, f); m_decls.emplace(key, f);
return f; return f;
@ -1121,31 +1144,29 @@ class tptp_parser {
// Coerce two expressions to have the same sort for equality. // Coerce two expressions to have the same sort for equality.
// In TPTP, = is term equality and m_univ is the default sort. // 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, returns lhs unchanged; otherwise applies minimal
// If sorts already match and are not Bool, returns lhs unchanged. // arithmetic/box coercions so both sides of an equality share a sort.
expr_ref coerce_eq(expr_ref lhs, expr_ref& rhs) { expr_ref coerce_eq(expr_ref lhs, expr_ref& rhs) {
// Coerce Bool-sorted operands to m_univ since = is term equality in TPTP // No coercion is needed when both sides already share a sort. In particular
if (m.is_bool(lhs->get_sort()) && is_app(lhs) && !m.is_true(lhs) && !m.is_false(lhs)) // `=` between two Boolean ($o) operands is logical equivalence (iff): keep
lhs = coerce_to_univ(lhs); // them Boolean instead of forcing one into the term universe U.
if (m.is_bool(rhs->get_sort()) && is_app(rhs) && !m.is_true(rhs) && !m.is_false(rhs)) if (lhs->get_sort() == rhs->get_sort())
rhs = coerce_to_univ(rhs); return lhs;
if (m_arith.is_int_real(lhs) && m_arith.is_int_real(rhs))
if (lhs->get_sort() == rhs->get_sort()) return lhs; return lhs;
// Coerce 0-arity constants to match the other side's sort // 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()); 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()); rhs = coerce_zero_arity(to_app(rhs), lhs->get_sort());
return lhs; return lhs;
} }
// Last resort: coerce both sides to have the same sort // 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
// Prefer coercing to rhs sort, falling back to m_univ sort* target = rhs->get_sort();
sort* target = rhs->get_sort(); lhs = coerce_arg(lhs, target);
lhs = coerce_arg(lhs, target);
}
return lhs; return lhs;
} }
@ -1176,7 +1197,7 @@ class tptp_parser {
// Bind parameter variables for parsing the RHS // Bind parameter variables for parsing the RHS
if (!param_scope.empty()) if (!param_scope.empty())
m_bound.push_back(param_scope); m_bound.push_back(param_scope);
expr_ref value = parse_formula(); expr_ref value = parse_formula(false);
if (!param_scope.empty()) if (!param_scope.empty())
m_bound.pop_back(); m_bound.pop_back();
// For function-style definitions, wrap value in lambdas // For function-style definitions, wrap value in lambdas
@ -1206,7 +1227,7 @@ class tptp_parser {
// --- Part 1: Parse type declarations --- // --- Part 1: Parse type declarations ---
std::vector<std::string> let_names; std::vector<std::string> let_names;
std::vector<sort*> let_sorts; ptr_vector<sort> let_sorts;
auto parse_one_typing = [&]() { auto parse_one_typing = [&]() {
std::string name = parse_name(); std::string name = parse_name();
@ -1264,7 +1285,7 @@ class tptp_parser {
// --- Part 3: Parse body with let-bound names in scope --- // --- Part 3: Parse body with let-bound names in scope ---
m_bound.push_back(scope); m_bound.push_back(scope);
expr_ref body = parse_formula(); expr_ref body = parse_formula(false);
m_bound.pop_back(); m_bound.pop_back();
expect(token_kind::rparen, "')'"); expect(token_kind::rparen, "')'");
@ -1288,7 +1309,7 @@ class tptp_parser {
// <defined_pred> ::= $less | $lesseq | $greater | $greatereq | $is_int | $is_rat | ... // <defined_pred> ::= $less | $lesseq | $greater | $greatereq | $is_int | $is_rat | ...
// <defined_infix_pred> ::= = | != // <defined_infix_pred> ::= = | !=
// Also handles: let-bound name resolution, implicit variable creation. // 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)) { if (accept(token_kind::lparen)) {
// Check for parenthesized connective used as higher-order term: (~), (&), (|), etc. // 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) || 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; token saved = m_curr;
next(); next();
if (accept(token_kind::rparen)) { 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* bool_sort = m.mk_bool_sort();
sort* ho_sort; symbol xn("X"), yn("Y");
if (arity == 1) if (arity == 1) {
ho_sort = m_array.mk_array_sort(bool_sort, bool_sort); // (~) ==> ^[X:$o] : ~X
else expr_ref body(m.mk_not(m.mk_var(0, bool_sort)), m);
ho_sort = m_array.mk_array_sort(bool_sort, m_array.mk_array_sort(bool_sort, bool_sort)); return expr_ref(m.mk_lambda(1, &bool_sort, &xn, body), m);
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);
} }
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 // Not a parenthesized connective — lparen was consumed and connective was consumed
// but ')' didn't follow. Parse as formula with the connective already consumed. // but ')' didn't follow. Parse as formula with the connective already consumed.
expr_ref inner(m); expr_ref inner(m);
if (saved.kind == token_kind::not_tok) { if (saved.kind == token_kind::not_tok) {
expr_ref e = parse_formula(); // "( ~ <formula> )": the '~' is a unary connective binding only the
inner = expr_ref(m.mk_not(e), m); // 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 { } else {
// Binary connective at start of parens — shouldn't happen in valid TPTP // Binary connective at start of parens — shouldn't happen in valid TPTP
throw parse_error("unexpected connective after '(' at " + loc()); throw parse_error("unexpected connective after '(' at " + loc());
@ -1342,12 +1382,15 @@ class tptp_parser {
// Parentheses create a new scope for @ consumption // Parentheses create a new scope for @ consumption
bool save_in_at_arg = m_in_at_arg; bool save_in_at_arg = m_in_at_arg;
m_in_at_arg = false; m_in_at_arg = false;
expr_ref e = parse_formula(); expr_ref e = parse_formula(is_boolean);
expect(token_kind::rparen, "')'"); expect(token_kind::rparen, "')'");
m_in_at_arg = save_in_at_arg; m_in_at_arg = save_in_at_arg;
return e; 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) // Handle negative numerals in formula position: -2 = $uminus(2)
if (accept(token_kind::minus_tok)) { if (accept(token_kind::minus_tok)) {
expr_ref t = parse_term(); expr_ref t = parse_term();
@ -1358,9 +1401,9 @@ class tptp_parser {
if (accept(token_kind::lbrack)) { if (accept(token_kind::lbrack)) {
if (accept(token_kind::rbrack)) if (accept(token_kind::rbrack))
return expr_ref(m.mk_const(symbol("$nil"), m_univ), m); 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)) while (accept(token_kind::comma))
parse_formula(); // consume remaining elements parse_formula(is_boolean); // consume remaining elements
expect(token_kind::rbrack, "']'"); expect(token_kind::rbrack, "']'");
return first; return first;
} }
@ -1373,6 +1416,7 @@ class tptp_parser {
return parse_numeral_from_name(n); 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) // Check if name is let-bound (works for both uppercase vars and lowercase let-bound names)
{ {
expr_ref b(m); expr_ref b(m);
@ -1417,7 +1461,7 @@ class tptp_parser {
} }
expect(token_kind::colon, "':'"); expect(token_kind::colon, "':'");
m_bound.push_back(scope); m_bound.push_back(scope);
expr_ref body = parse_formula(); expr_ref body = parse_formula(is_boolean);
m_bound.pop_back(); m_bound.pop_back();
// Approximate choice as existential quantification // Approximate choice as existential quantification
return mk_quantifier(false, vars, body); 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) // $ite needs special parsing: first arg is formula, rest are formulas (branches can be equalities)
if (n == "$ite") { if (n == "$ite") {
expect(token_kind::lparen, "'('"); expect(token_kind::lparen, "'('");
args.push_back(parse_formula()); args.push_back(parse_formula(true));
expect(token_kind::comma, "','"); expect(token_kind::comma, "','");
args.push_back(parse_formula()); args.push_back(parse_formula(is_boolean));
expect(token_kind::comma, "','"); expect(token_kind::comma, "','");
args.push_back(parse_formula()); args.push_back(parse_formula(is_boolean));
expect(token_kind::rparen, "')'"); expect(token_kind::rparen, "')'");
} }
else if (n == "$let") { else if (n == "$let") {
@ -1465,18 +1509,19 @@ class tptp_parser {
auto typed = m_typed_decls.find(mk_typed_key(n, args.size())); auto typed = m_typed_decls.find(mk_typed_key(n, args.size()));
if (typed != m_typed_decls.end()) { 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); 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); coerce_args(f, args);
return expr_ref(args.empty() ? m.mk_const(f) : m.mk_app(f, args.size(), args.data()), m); 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))) { is_boolean = is_boolean && !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);
}
func_decl* pred = mk_decl_or_ho_const(n, args.size(), true);
if (!args.empty()) coerce_args(pred, args); func_decl* pred = mk_decl_or_ho_const(n, args.size(), is_boolean);
return expr_ref(args.empty() ? m.mk_const(pred) : m.mk_app(pred, args.size(), args.data()), m); 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> // 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 // Lambda body does NOT consume @ — @ belongs to the enclosing application
bool save_in_at_arg = m_in_at_arg; bool save_in_at_arg = m_in_at_arg;
m_in_at_arg = true; 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_in_at_arg = save_in_at_arg;
m_bound.pop_back(); m_bound.pop_back();
if (vars.empty()) if (vars.empty())
@ -1538,9 +1583,9 @@ class tptp_parser {
// <thf_quantified_formula> ::= <thf_quantification> <thf_unitary_formula> // <thf_quantified_formula> ::= <thf_quantification> <thf_unitary_formula>
// <fof_quantifier> ::= ! | ? // <fof_quantifier> ::= ! | ?
// Also handles: $ite, $let, lambda (^), parenthesized formulas, and atomic formulas. // 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)) { 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); return expr_ref(m.mk_not(ensure_bool(e)), m);
} }
@ -1552,7 +1597,7 @@ class tptp_parser {
next(); // consume '[' next(); // consume '['
if (accept(token_kind::dot)) { if (accept(token_kind::dot)) {
expect(token_kind::rbrack, "']'"); 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"); func_decl* f = mk_modal_op("box");
return expr_ref(m.mk_app(f, sub.get()), m); return expr_ref(m.mk_app(f, sub.get()), m);
} }
@ -1561,7 +1606,7 @@ class tptp_parser {
std::string first_name = m_curr.text; std::string first_name = m_curr.text;
next(); next();
if (accept(token_kind::rbrack)) { 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); func_decl* f = mk_modal_op(mod_name);
return expr_ref(m.mk_app(f, sub.get()), m); 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)) else if (should_create_implicit_var(first_name))
first = expr_ref(get_or_create_implicit_var(first_name), m); first = expr_ref(get_or_create_implicit_var(first_name), m);
else { 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); first = expr_ref(m.mk_const(f), m);
} }
while (accept(token_kind::comma)) while (accept(token_kind::comma))
parse_formula(); // consume remaining elements parse_formula(is_boolean); // consume remaining elements
expect(token_kind::rbrack, "']'"); expect(token_kind::rbrack, "']'");
return first; return first;
} }
@ -1587,9 +1632,9 @@ class tptp_parser {
// We already consumed '[', so parse as tuple inline // We already consumed '[', so parse as tuple inline
if (accept(token_kind::rbrack)) if (accept(token_kind::rbrack))
return expr_ref(m.mk_const(symbol("$nil"), m_univ), m); 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)) while (accept(token_kind::comma))
parse_formula(); // consume remaining elements parse_formula(is_boolean); // consume remaining elements
expect(token_kind::rbrack, "']'"); expect(token_kind::rbrack, "']'");
return first; return first;
} }
@ -1605,7 +1650,7 @@ class tptp_parser {
next(); next();
} }
expect(token_kind::gt_tok, "'>'"); 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); func_decl* f = mk_modal_op(mod_name);
return expr_ref(m.mk_app(f, sub.get()), m); return expr_ref(m.mk_app(f, sub.get()), m);
} }
@ -1652,7 +1697,13 @@ class tptp_parser {
} }
expect(token_kind::colon, "':'"); expect(token_kind::colon, "':'");
m_bound.push_back(scope); 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(); m_bound.pop_back();
return mk_quantifier(is_forall, vars, body); return mk_quantifier(is_forall, vars, body);
} }
@ -1672,10 +1723,10 @@ class tptp_parser {
expect(token_kind::rbrack, "']'"); expect(token_kind::rbrack, "']'");
} }
expect(token_kind::colon, "':'"); 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> // 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> & <tff_unit_formula>
// | <tff_and_formula> & <tff_unit_formula> // | <tff_and_formula> & <tff_unit_formula>
// Implements a Pratt-style (precedence climbing) parser for binary connectives. // Implements a Pratt-style (precedence climbing) parser for binary connectives.
expr_ref parse_expr(unsigned min_prec, bool consume_at = true) { expr_ref parse_expr(unsigned min_prec, bool consume_at, bool is_boolean) {
expr_ref e = parse_unary_formula(); 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 (;;) { for (;;) {
// Handle @ (function application) with highest precedence // Handle @ (function application) with highest precedence
// But NOT when we're inside a lambda body that's an @ argument // 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; if (it->second.precedence < min_prec) break;
next(); // consume the operator token next(); // consume the operator token
unsigned next_prec = it->second.right_assoc ? it->second.precedence : it->second.precedence + 1; 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); expr_ref_vector args(m);
args.push_back(e); args.push_back(e);
args.push_back(rhs); args.push_back(rhs);
@ -1786,12 +1852,27 @@ class tptp_parser {
// Try relative to current file's directory // Try relative to current file's directory
std::string local = normalize_path(dirname(curr_file) + "/" + name); std::string local = normalize_path(dirname(curr_file) + "/" + name);
if (file_exists(local)) return local; 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"); char const* root = std::getenv("TPTP");
if (root) { if (root) {
std::string env = normalize_path(std::string(root) + "/" + name); std::string env = normalize_path(std::string(root) + "/" + name);
if (file_exists(env)) return env; 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) // Try relative to current working directory (common when running from TPTP root)
std::string cwd_relative = normalize_path(name); std::string cwd_relative = normalize_path(name);
if (file_exists(cwd_relative)) return cwd_relative; if (file_exists(cwd_relative)) return cwd_relative;
@ -1825,7 +1906,7 @@ class tptp_parser {
// <annotations> ::= ,<source><optional_info> | <null> // <annotations> ::= ,<source><optional_info> | <null>
void parse_annotated() { void parse_annotated() {
expect(token_kind::lparen, "'('"); expect(token_kind::lparen, "'('");
parse_name(); std::string formula_name = parse_name();
expect(token_kind::comma, "','"); expect(token_kind::comma, "','");
std::string role = to_lower(parse_name()); std::string role = to_lower(parse_name());
expect(token_kind::comma, "','"); expect(token_kind::comma, "','");
@ -1836,12 +1917,14 @@ class tptp_parser {
else if (role == "logic") { else if (role == "logic") {
// Modal logic declarations ($modal == [...]) — skip the formula body // Modal logic declarations ($modal == [...]) — skip the formula body
skip_annotations_until_rparen(); skip_annotations_until_rparen();
warning_msg("non-classical logics are not supported");
++m_dropped_formulas;
} }
else { else {
try { try {
implicit_var_scope implicit_scope; implicit_var_scope implicit_scope;
scoped_implicit_vars scoped(*this, 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()) { if (!implicit_scope.order.empty()) {
f = mk_quantifier(true, implicit_scope.order, f); f = mk_quantifier(true, implicit_scope.order, f);
} }
@ -1854,13 +1937,30 @@ class tptp_parser {
} }
m_cmd.assert_expr(f); m_cmd.assert_expr(f);
} catch (z3_exception const& ex) { } catch (z3_exception const& ex) {
// Sort mismatch or other semantic error in this formula — skip it // Sort mismatch or other semantic error in this formula — skip it.
IF_VERBOSE(2, verbose_stream() << "skipping formula due to: " << ex.what() << "\n"); // 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 // Skip to '.' to resync the parser for the next annotated formula
while (!is(token_kind::eof_tok) && !is(token_kind::dot)) while (!is(token_kind::eof_tok) && !is(token_kind::dot))
next(); next();
if (is(token_kind::dot)) next(); if (is(token_kind::dot)) next();
return; 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) { 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. // Save parser state so that included files don't clobber the caller's lexer.
std::string saved_input = std::move(m_input); std::string saved_input = std::move(m_input);
@ -2123,6 +2229,7 @@ public:
std::ostringstream buf; std::ostringstream buf;
buf << in.rdbuf(); buf << in.rdbuf();
m_input = buf.str(); m_input = buf.str();
extract_expected_status(m_input);
m_lex = std::make_unique<lexer>(m_input); m_lex = std::make_unique<lexer>(m_input);
next(); next();
parse_toplevel(current_file); parse_toplevel(current_file);
@ -2149,6 +2256,78 @@ public:
} }
bool has_conjecture() const { return m_has_conjecture; } 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() { expr_ref tptp_parser::parse_term() {
@ -2172,12 +2351,41 @@ expr_ref tptp_parser::parse_term() {
return e; return e;
} }
expr_ref tptp_parser::parse_formula() { expr_ref tptp_parser::parse_formula(bool is_boolean) {
return parse_expr(PREC_IFF); 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) { static unsigned read_tptp_stream(std::istream& in, char const* current_file) {
register_on_timeout_proc(on_timeout); register_on_timeout_proc(on_timeout);
try { try {
@ -2186,6 +2394,7 @@ static unsigned read_tptp_stream(std::istream& in, char const* current_file) {
tptp_parser p(ctx); tptp_parser p(ctx);
p.parse_input(in, current_file ? current_file : "."); p.parse_input(in, current_file ? current_file : ".");
p.assert_distinct_objects();
// Suppress default check-sat output; TPTP frontend reports SZS status explicitly. // Suppress default check-sat output; TPTP frontend reports SZS status explicitly.
std::ostringstream sink; std::ostringstream sink;
@ -2194,12 +2403,24 @@ static unsigned read_tptp_stream(std::istream& in, char const* current_file) {
ctx.check_sat(0, nullptr); ctx.check_sat(0, nullptr);
switch (ctx.cs_state()) { switch (ctx.cs_state()) {
case cmd_context::css_unsat: case cmd_context::css_unsat:
if (p.has_conjecture()) std::cout << "% SZS status Theorem\n"; if (p.has_conjecture()) report_szs_status("Theorem", p.expected_status());
else std::cout << "% SZS status Unsatisfiable\n"; else report_szs_status("Unsatisfiable", p.expected_status());
break; break;
case cmd_context::css_sat: case cmd_context::css_sat:
if (p.has_conjecture()) std::cout << "% SZS status CounterSatisfiable\n"; // A "sat" verdict is only sound if the whole problem was encoded. If any
else std::cout << "% SZS status Satisfiable\n"; // 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) { if (g_display_model) {
model_ref mdl; model_ref mdl;
if (ctx.is_model_available(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) { 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(); 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]() { auto explain = [row, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, lar]() {
(void) strict; (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";); 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; int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign;
u_dependency* ret = nullptr; u_dependency* ret = nullptr;
for (auto const& r : row) { for (auto const& r : *row) {
unsigned j = r.var(); unsigned j = r.var();
if (j == bound_j) if (j == bound_j)
continue; continue;

View file

@ -580,7 +580,7 @@ namespace lp {
const lar_term* m_t; const lar_term* m_t;
undo_add_term(imp& s, const lar_term* t) : m_s(s), m_t(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); m_s.undo_add_term_method(m_t);
} }
}; };

View file

@ -16,6 +16,9 @@ Author:
Revision History: Revision History:
--*/ --*/
#include <algorithm>
#include <unordered_map>
#include "math/lp/int_solver.h" #include "math/lp/int_solver.h"
#include "math/lp/lar_solver.h" #include "math/lp/lar_solver.h"
#include "math/lp/int_cube.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()); 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 { // The largest cube test of Bromberger and Weidenbach:
if (t.size() == 2) { // maximize x_e subject to Ax + a'(x_e/2) <= b, x_e >= 0, where a'_i = ||a_i||_1,
bool seen_minus = false; // with the 1-norm taken over the integer variables of the row.
bool seen_plus = false; // The solution is the center z of a largest cube contained in the polyhedron.
for(lar_term::ival p : t) { // If the maximal edge length is at least 1, then the rounding of z is
if (!lia.column_is_int(p.j())) // an integer solution; otherwise the rounding is checked, and possibly repaired,
goto usual_delta; // against the original constraints.
const mpq & c = p.coeff(); lia_move int_cube::find_largest_cube() {
if (c == one_of_type<mpq>()) { lia.settings().stats().m_lcube_calls++;
seen_plus = true; TRACE(cube,
} else if (c == -one_of_type<mpq>()) { for (unsigned j = 0; j < lra.number_of_vars(); ++j)
seen_minus = true; lia.display_column(tout, j);
} else { tout << lra.constraints();
goto usual_delta; );
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) if (best_fi == UINT_MAX)
return zero_of_type<impq>(); return false; // no flip decreases the violation
return impq(0, 1); 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>(); mpq delta = zero_of_type<mpq>();
for (lar_term::ival p : t) for (lar_term::ival p : t)
if (lia.column_is_int(p.j())) if (lia.column_is_int(p.j()))
delta += abs(p.coeff()); delta += abs(p.coeff());
delta *= mpq(1, 2); delta *= mpq(1, 2);
return impq(delta); return impq(delta);
} }

View file

@ -13,6 +13,12 @@ Abstract:
by tightnening bounds and running an LRA solver on the by tightnening bounds and running an LRA solver on the
tighter system. 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: Author:
Nikolaj Bjorner (nbjorner) Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach) Lev Nachmanson (levnach)
@ -21,7 +27,10 @@ Revision History:
--*/ --*/
#pragma once #pragma once
#include "util/vector.h"
#include "math/lp/lia_move.h" #include "math/lp/lia_move.h"
#include "math/lp/numeric_pair.h"
#include "math/lp/lar_term.h"
namespace lp { namespace lp {
class int_solver; class int_solver;
@ -29,12 +38,30 @@ namespace lp {
class int_cube { class int_cube {
class int_solver& lia; class int_solver& lia;
class lar_solver& lra; 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_term_for_cube(unsigned i);
bool tighten_terms_for_cube(); bool tighten_terms_for_cube();
void find_feasible_solution(); void find_feasible_solution();
impq get_cube_delta_for_term(const lar_term& t) const; 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: public:
int_cube(int_solver& lia); int_cube(int_solver& lia);
lia_move operator()(); lia_move operator()();
lia_move find_largest_cube();
}; };
} }

View file

@ -44,6 +44,10 @@ namespace lp {
dioph_eq m_dio; dioph_eq m_dio;
int_gcd_test m_gcd; int_gcd_test m_gcd;
unsigned m_initial_dio_calls_period; 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 { bool column_is_int_inf(unsigned j) const {
return lra.column_is_int(j) && (!lia.value_is_int(j)); return lra.column_is_int(j) && (!lia.value_is_int(j));
@ -52,6 +56,7 @@ namespace lp {
imp(int_solver& lia): lia(lia), lra(lia.lra), lrac(lia.lrac), m_hnf_cutter(lia), m_dio(lia), m_gcd(lia) { 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_hnf_cut_period = settings().hnf_cut_period();
m_initial_dio_calls_period = settings().dio_calls_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 { bool has_lower(unsigned j) const {
@ -176,14 +181,16 @@ namespace lp {
if (r == lia_move::conflict) { if (r == lia_move::conflict) {
m_dio.explain(*this->m_ex); m_dio.explain(*this->m_ex);
lia.settings().dio_calls_period() = m_initial_dio_calls_period; 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); lia.settings().set_run_gcd_test(false);
return lia_move::conflict; return lia_move::conflict;
} }
if (r == lia_move::undef) { if (r == lia_move::undef) {
lia.settings().dio_calls_period() *= 2; lia.settings().dio_calls_period() *= 2; // throttle dio scheduling on failure
if (lra.settings().dio_calls_period() >= 16) { ++m_dio_undef_in_a_row;
lia.settings().dio_enable_gomory_cuts() = true; 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); lia.settings().set_run_gcd_test(true);
} }
} }
@ -192,25 +199,65 @@ namespace lp {
lp_settings& settings() { return lra.settings(); } 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() { 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 should_gomory_cut() {
bool dio_allows_gomory = !settings().dio() || settings().dio_enable_gomory_cuts() || bool dio_allows_gomory = !settings().dio() || settings().dio_enable_gomory_cuts() ||
m_dio.some_terms_are_ignored(); 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() { 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 // HNF
bool should_hnf_cut() { bool should_hnf_cut() {
return (!settings().dio() || settings().dio_enable_hnf_cuts()) 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() { lia_move hnf_cut() {
@ -245,11 +292,12 @@ namespace lp {
++m_number_of_calls; ++m_number_of_calls;
if (r == lia_move::undef) r = patch_basic_columns(); 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) lra.move_non_basic_columns_to_bounds();
if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut(); 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_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 (r == lia_move::undef) r = int_branch(lia)();
if (settings().get_cancel_flag()) r = lia_move::undef; if (settings().get_cancel_flag()) r = lia_move::undef;
return r; return r;

View file

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

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