---
title: 'Attach-Stobj: Linking Abstract & Concrete Stobjs'
url: https://www.emergentmind.com/topics/attach-stobj
type: topic
---

# Attach-Stobj: Linking Abstract & Concrete Stobjs

Searching arXiv for the cited ACL2 papers to ground the article.
Attach-Stobj denotes two closely related ACL2 ideas. In the earlier abstract-stobj literature, it is a useful description of how `defabsstobj` links a fast concrete single-threaded object to a logically simple abstract interface through paired `:logic` and `:exec` exports plus a correspondence relation [1304.7858]. In later ACL2 terminology, `attach-stobj` is a feature that “first appeared in ACL2 Version 8.6 (October, 2024)” and allows an abstract stobj declared with `:attachable t` to execute using the `:exec` and `:foundation` of a separately defined implementation stobj, without recertifying the book that defines the original abstract stobj or theorems about it [2508.00016]. Across both senses, the central objective is stable logical reasoning over one state abstraction together with efficient or replaceable execution over another.

## 1. Conceptual basis in abstract stobjs

In ACL2, a stobj is a mutable object with applicative semantics that ACL2 executes efficiently while preserving logical soundness via a single-threadedness discipline. Concrete stobjs are introduced by `defstobj`; fields can have types, array constraints, and initial values, and the resulting representation is optimized for execution [1304.7858]. The principal limitation described for concrete stobjs is that invariants are naturally field-wise: cross-field invariants are not expressible in the `defstobj` recognizer and instead appear as heavy guard obligations or hypotheses in theorems [1304.7858].

An abstract stobj, introduced by `defabsstobj`, provides an alternative logical representation and interface for an existing concrete stobj. Its exported operations have two views: `:logic` functions, used for reasoning, and `:exec` functions, used for execution. A correspondence predicate relates abstract and concrete states, and `defabsstobj` generates obligations ensuring that initial states correspond, exported operations preserve correspondence, and guards on abstract operations imply the guards of the concrete operations that will actually run [1304.7858].

This is the sense in which abstract stobjs realize an “Attach-Stobj” idea. The attachment is not logical equality, but a disciplined correspondence between two state representations. The abstract side can be small, sparse, and invariant-rich; the concrete side can be array-based, mutable, and optimized for raw Lisp execution. This separation yields faster execution, more efficient reasoning, support for symbolic simulation, and resilience of proof developments under concrete modeling optimization [1304.7858].

## 2. Formal mechanism of `defabsstobj`

The core `defabsstobj` pattern pairs a new abstract stobj name with an existing concrete stobj and specifies recognizers, creators, a correspondence function, and exports. A representative form is given as follows [1304.7858]:

```lisp
(defabsstobj ST
  :concrete st$c
  :recognizer (stp :logic st$ap :exec st$cp)
  :creator    (create-st :logic create-st$a :exec create-st$c
                         :correspondence create-st{correspondence}
                         :preserved      create-st{preserved})
  :corr-fn st$corr
  :exports
    ((LOOKUP  :logic lookup$a  :exec mem$ci
              :correspondence lookup{correspondence}
              :guard-thm     lookup{guard-thm})
     (UPDATE  :logic update$a  :exec update-mem$ci
              :correspondence update{correspondence}
              :preserved      update{preserved}
              :guard-thm      update{guard-thm})
     (MISC    :logic misc$a    :exec misc$c
              :correspondence misc{correspondence})
     (UPDATE-MISC :logic update-misc$a :exec update-misc$c
                  :correspondence update-misc{correspondence}
                  :preserved      update-misc{preserved})))
```

Before admitting such an event, one defines the abstract recognizer and creator, abstract operation specifications, the correspondence predicate, and the concrete side created by `defstobj`. What `defabsstobj` generates and requires includes `{CORRESPONDENCE}` theorems, `{PRESERVED}` theorems, and `{GUARD-THM}` theorems [1304.7858].

The correspondence can be expressed in a bisimulation-style form. Let $\phi(C,A)$ relate a concrete state $C$ to an abstract state $A$. Then initialization requires $\phi(\mathrm{create}_C(), \mathrm{create}_A())$. For a reader $f$, the requirement is that $\phi(C,A)$ implies equality of returned values. For an updater, the paper gives the update law in LaTeX form as
$$
\phi(C, A) \Rightarrow \phi(f_C(C, x), f_A(A, x)).
$$
Recognizer preservation and guard transfer complete the discipline: the abstract recognizer must hold initially and be preserved by abstract updates, and abstract guards must entail the concrete guards needed by the executing `:exec` functions [1304.7858].

A representative example models abstract memory as an alist from indices to even naturals, with state represented as a cons `(misc . mem-map)`, while execution uses a concrete array-based `mem$c`. The correspondence asserts equality between concrete reads and abstract lookup for each address in range, as well as equality of the miscellaneous field [1304.7858]. This arrangement permits rich abstract invariants such as “all memory values are even” without forcing those invariants to be rechecked during execution.

## 3. Guards, single-threadedness, and atomicity

The exports of an abstract stobj introduce stobj signatures that enforce single-threaded use of the abstract stobj, whereas the logical `$a` functions retain ordinary non-stobj signatures. This division is central to keeping proofs simple while maintaining executable discipline [1304.7858].

Guard handling is stricter than ordinary guard elision. Abstract functions may express high-level constraints such as index bounds or value parity, but `defabsstobj` requires proof that these imply the concrete guards of the underlying `:exec` functions. Even with guard-checking off, ACL2 always enforces guards for exported abstract stobj functions “to ensure compliant live stobj manipulation” [1304.7858]. The abstract recognizer is defined to evaluate to `T` in raw Lisp when applied to a stobj, which avoids costly invariant checks at runtime while retaining logical strength for proofs [1304.7858].

A further issue is atomicity of executable exports. ACL2 6.0+ requires `:protect t` for any export whose `:EXEC` may be non-atomic. The stated pitfall is that an executable exported function performing multiple updates may be interrupted, for example by an error, leaving the stobj in a state that violates the abstract recognizer. In response, ACL2 inserts runtime checks and can signal an error, disabling certification for the session, if an incomplete export left the stobj unprotected [1304.7858]. Syntactic analysis may allow omission of `:protect t` for obviously atomic exports, defined as those with at most one updater call [1304.7858].

These constraints are especially important when the abstract interface is intended to serve as the stable logical boundary for larger verification developments. Forgetting a `{PRESERVED}` theorem for an updater, misaligning field semantics between abstract and concrete states, or depending on execution of the abstract recognizer for runtime checking are all identified as common mistakes [1304.7858].

## 4. ISA modeling and symbolic execution

The best-known case study in the original abstract-stobj work is instruction-set architecture modeling for Y86/x86-32. The concrete processor stobj includes registers, `EIP/PC`, flags, and a space-efficient, two-level memory with on-demand allocation: `mem-table`, `mem-array`, and `mem-array-next-addr` [1304.7858]. Concrete writes may resize the large memory array and rely on a well-formedness invariant `good-memp` enforced by the concrete recognizer [1304.7858].

For symbolic execution, the concrete-only model is problematic because the logical representation of stobjs is a massive list of fields and arrays. GL’s symbolic interpreter would need to construct an enormous `mem-array` list, causing value stack overflows and timeouts [1304.7858]. The abstract x86-32 stobj addresses this by representing memory as a sparse record with initial value `nil`, using a recognizer defined with `defun-sk`:

```lisp
(defun-sk memp (x)
  (forall i (implies (g i x) (and (n32p i) (n08p (g i x))))))
```

Abstract memory write is correspondingly small:

```lisp
(defun !mem$ai (i v x86-32)
  (update-nth *memi* (s i v (nth *memi* x86-32)) x86-32))
```

The abstract stobj is exported with `defabsstobj`, including `:protect t` on the memory updater because the concrete executable write may be non-atomic [1304.7858]. Memory correspondence is stated pointwise: for all in-range addresses, concrete read `mem$ci(i, C)` equals abstract record lookup `g0(i, AbsMem(A))` [1304.7858].

This design simplifies rewrite theory. A representative unconditional read-over-write theorem is:

```lisp
(defthm read-write
  (equal (memi i (!memi j v x86-32))
         (if (equal i j) (or v 0) (memi i x86-32))))
```

The practical consequence is that the Y86 “popcount” proof via GL symbolic execution succeeds on the abstract-stobj model in approximately 29 seconds on a 2.2 GHz Intel Core i7 / 8GB RAM under ACL2 6.0 and Clozure CL, with no auxiliary lemmas or custom clause processors, whereas the same style of GL proof failed for the concrete-only model because of massive mem-array lists causing stack overflow and timeouts [1304.7858].

## 5. The ACL2 8.6 `attach-stobj` feature

The later ACL2 feature `attach-stobj` formalizes a narrower but powerful notion of replaceable execution. It “first appeared in ACL2 Version 8.6 (October, 2024)” and allows different ACL2 sessions to specify different ways to execute operations on an attachable abstract stobj without recertifying the book that defines that stobj [2508.00016]. The mechanism is enabled by declaring the original stobj with `:attachable t`:

```lisp
(defabsstobj ST
  ...
  :attachable t)
```

An implementation stobj is then defined first, followed by an attachment event, followed by inclusion or definition of the attachable stobj:

```lisp
(defabsstobj IMPL ...)
(attach-stobj ST IMPL)
(include-book "B_ST")
```

The required order is explicit: the implementation stobj must already be defined, and the attach-stobj event must precede the `defabsstobj` event that defines `ST` [2508.00016]. The key semantic operation is to “replace the :foundation and the :exec fields of the attachable stobj with those of its implementation” [2508.00016].

This substitution changes execution only. Logical functions and theorems remain those of the attachable stobj, which is why existing book certifications remain valid [2508.00016]. The mechanism therefore supports “Mutable Objects with Several Implementations”: a single logical stobj can be executed through different implementation stobjs in different ACL2 sessions [2508.00016].

A strict compatibility condition is imposed: the attachable and implementation stobjs must have “the same sequence of :logic functions,” meaning the same primitives in the same order [2508.00016]. Attachable abstract stobjs must still satisfy the usual abstract-stobj requirements, including complete `:exec` fields, even if those fields will later be overridden by attachment [2508.00016].

## 6. Implementation architecture, nested stobjs, and performance trade-offs

Internally, `attach-stobj` populates a world table named `attach-stobj-table`, mapping an attachable stobj name to an implementation stobj name. Later, when the attachable stobj is defined, ACL2 recursively resolves the implementation by consulting this table [2508.00016]. The paper gives the internal lookup function as:

```lisp
(defun attached-stobj (st wrld top)   ; Top is t for a top-level call, nil otherwise.
  (let ((st2 (cdr (assoc-eq st (table-alist 'attach-stobj-table wrld)))))
    (cond (st2 (attached-stobj st2 wrld nil))
          (top nil)
          (t st))))
```

The core implementation function `defabsstobj-fn1` then effectively invokes itself recursively when `:attachable t` is supplied, replacing `:exec` and `:foundation` after checking the identity of the logical interfaces [2508.00016]. Because abstract-stobj primitives are often macros, ACL2 also manages compilation carefully. Using two globals, `ext-gens` and `ext-gen-barriers`, ACL2 arranges that compiled code from a book should be ignored so that macroexpansion targets the attached implementation’s `:exec` functions at include-book time [2508.00016].

The feature works in nested-stobj settings, including when the child is “whether concrete or abstract” [2508.00016]. A typical pattern is to attach a memory stobj implementation before defining a larger `X86` stobj that includes the memory stobj as a child. The implementation stobj may also use `:non-executable t` if it will serve only as an implementation or only as a child or local stobj, thereby preventing creation of a global implementation stobj unless `add-global-stobj` is later used [2508.00016].

The stated engineering trade-off is that execution with attachable stobjs is efficient because `attach-stobj` introduces no indirection, but compilation is performed at include-book time when existing compiled code is avoided [2508.00016]. The feature is illustrated through alternative memory models, specifically “symmetric” and “asymmetric” big memory implementations. For 100,000 writes of byte 1 to random addresses in a range of $2^{30}$, the symmetric model is reported as best for “high” writes, while the other two setups are best for “low” writes; `attach-stobj` allows one to retain theorems about the symmetric model and attach the asymmetric model for performance [2508.00016].

## 7. Related attachment patterns and broader significance

The idea of attaching execution to an abstract state interface also appears in the integration of external incremental SAT solvers. In “Incremental SAT Library Integration Using Abstract Stobjs,” the solver is modeled as an abstract stobj named `ipasir`, whose logical state includes fields such as `formula`, `assumption`, `new-clause`, `status`, `solution`, `solved-assumption`, `callback-count`, and `history` [1810.04311]. The logical model is used for proofs, while execution is realized through raw Lisp/CFFI wrappers to an external IPASIR solver library [1810.04311].

The paper explicitly notes that there is no literal `attach-stobj` primitive in that design. Instead, the executable versions of the abstract stobj’s exported functions are redefined to call into the external library, and one constrained function, `ipasir-signature`, uses `defattach` [1810.04311]. The authors describe this as an “attach-stobj” pattern: `defabsstobj` provides the logical API, while the backend attaches raw executable bodies to exported abstract-stobj functions [1810.04311].

This example clarifies the boundaries between several ACL2 mechanisms. `defattach` attaches an executable function to a constrained function for execution, but it is function-oriented and does not address state, single-threadedness, or correspondence obligations [1304.7858]. `mbe` and `defexec` provide `:LOGIC` and `:EXEC` versions of the same function that are logically equal, whereas abstract stobjs allow `:LOGIC` and `:EXEC` components to correspond via a relation $\phi$ rather than equality [1304.7858]. Raw Lisp redefinitions are possible but are characterized as fragile and potentially threatening to soundness; abstract stobjs and, later, `attach-stobj` provide supported mechanisms with generated obligations or implementation checks [1304.7858].

A common misconception is to treat Attach-Stobj as a single ACL2 primitive throughout the literature. The source record supports a sharper distinction. In the 2013 work, “Attach-Stobj” is an apt description of the abstract-stobj discipline rather than a term used by the paper itself [1304.7858]. In the 2025 extended abstract, `attach-stobj` is a specific ACL2 feature with concrete syntax, order-of-events constraints, world-table support, and session-specific execution substitution [2508.00016]. A plausible implication is that the later feature systematizes one important execution-substitution use case while preserving the broader abstract-stobj methodology that originally made such separation between logic and execution technically viable.

Source: https://www.emergentmind.com/topics/attach-stobj