bhc-query
Query-based compilation system for the Basel Haskell Compiler.
Overview
This crate provides the infrastructure for incremental, demand-driven compilation using a query-based architecture. It enables automatic memoization, dependency tracking, and cycle detection.
Features
- Incremental recomputation: Only recompute what has changed
- Demand-driven evaluation: Queries computed only when needed
- Automatic memoization: Query results are cached
- Cycle detection: Detect and report recursive query cycles
- Parallel evaluation: Thread-safe query execution
Architecture
┌─────────────────────────────────────────────────────┐
│ QueryDatabase │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Query A │ │ Query B │ │ Query C │ │
│ │ (cached) │──│ (cached) │──│ (pending) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ Dependencies │
└─────────────────────────────────────────────────────┘
Key Types
| Type | Description |
|---|---|
QueryDatabase |
Central database storing query results |
Revision |
Version number for tracking changes |
QueryId |
Unique identifier for a query invocation |
QueryState |
Current state of a query (NotComputed, InProgress, Memoized, Stale) |
QueryError |
Errors including cycles and panics |
Usage
Defining Queries
use ;
// Define a query trait
// Define a query that depends on another
Query Execution
use ;
let mut db = new;
// Execute a query (will be computed and cached)
let result = db.;
// Re-execute (will use cached result if inputs unchanged)
let cached = db.;
assert_eq!;
Incremental Updates
// Mark an input as changed
db.set_input;
// Queries depending on this input are now stale
// Next query will recompute only what's necessary
let fresh = db.;
Revision Tracking
use Revision;
let rev = INITIAL;
assert_eq!;
let next = rev.next;
assert_eq!;
Query States
| State | Description |
|---|---|
NotComputed |
Query has never been executed |
InProgress |
Query is currently being computed |
Memoized(rev) |
Query was computed in revision rev |
Stale |
Query needs recomputation |
Error Handling
use QueryError;
match db.
Cycle Detection
The query system automatically detects cycles:
// If Query A depends on Query B, and Query B depends on Query A:
// QueryError::Cycle([QueryId(A), QueryId(B)]) is returned
Thread Safety
Queries can be executed from multiple threads:
use thread;
let db = new;
let handles: = files.iter.map.collect;
Design Notes
- Based on the salsa/rustc query model
- Uses interior mutability for concurrent access
- Revision numbers enable cheap staleness checks
- Query keys must be hashable and equality-comparable
Related Crates
bhc-driver- Uses queries for compilation orchestrationbhc-session- Session context for queriesbhc-typeck- Type checking queriesbhc-parser- Parsing queries
Specification References
- H26-SPEC Section 11: Incremental Compilation