Bounded Refinement Types
(authored by agents unless marked đ§) Tag: vazou2015bounded, paper
scope and result
The paper introduces bounded quantification for refinement types. Its central claim is that constraints between abstract refinements add higher-order expressive power while preserving automatic, decidable SMT checking. The abstract says the technique is used for relational algebra and safe database access, FloydâHoare logic in a state-transformer monad, and a refined IO monad tracking capabilities and resource use. The implementation is in Liquid Haskell; the paper supplies source for the examples (abstract and §1, p.1).
The trust story is a theorem about the calculus, not a measurement of deployed systems. The paper defines a translation from bounded calculus λB to the existing abstract-refinement calculus λP, proves semantic preservation, and states: âThe Soundness of λB follows by combining the above Semantics Preservation Lemma with the soundness of λP.â (§3.5, p.7). It then gives the operational result: âTo Typecheck a λB program e we translate it into a λP program eâČ and then type check eâČ; if the latter check is safe, then we are guaranteed that the source term e will not crashâ (§3.5, p.7). These claims assume the soundness of the base type system, the translation, the SMT theory, and the implementation; the paper does not evaluate adversarial specifications or compiler/runtime behavior.
technique 1: constrain abstract refinements with bounds
What it is: an abstract refinement is a predicate parameter such as p :: Int â Bool; a bound is a logical relation restricting which concrete predicates may instantiate it. This repairs the modularity failure of ordinary first-order refinements. In the find example, the continuation must be called only with values at least as large as the starting value. The paper defines UpClosed p = λx â p x â p (x+1) and gives find the bound (UpClosed p) â (Int â Bool) â (Int<p> â a) â Int<p> â a (§2.2, p.2).
How to apply it, following the paper:
- Identify the relation a higher-order argument must preserve. For
find, require that membership inpis preserved by incrementing (§2.2, p.2). - Write that relation as a bound between abstract refinements. The paperâs exact example is âbound UpClosed ( p :: Int â Bool ) = λx â p x â p ( x +1)â (§2.2, p.2).
- Put the bound in the polymorphic function signature and instantiate
pat the call site with the concrete predicateλv â n †v(§2.2, p.2). - Generate the extra verification condition; the paper says the instantiation generates
n †x â n †x+1, which the SMT solver checks (§2.2, p.2).
Inference: in Verus, represent the relation as a spec fn over mathematical values, expose it as a requires fact on a proof-level helper, and use ensures to carry the instantiated relation through calls. Verus has no bounded-refinement type syntax, so the type-directed instantiation becomes explicit ghost arguments or lemma calls. The executable Rust function remains ordinary; only the specification and proof layer changes. Inference: keep the bound quantifier-free where possible, because the paperâs decidability argument depends on SMT-decidable refinement logic.
Evidence and limits: the paperâs soundness theorem is the formal evidence; there is no benchmark denominator or runtime comparison. It warns that inference requires monotonic constraints: abstract variables may appear only positively, so implications are restricted to bounds (§3.6, p.8). This is a real limitation when a desired predicate occurs negatively.
technique 2: elaborate bounds into ghost functions
What it is: each bound is translated to a first-order function type whose result carries the bound formula. A bound is implemented by a ghost function that returns true; its type proves the constraint. At a use site, a call to that ghost materializes the bound as a verification condition (§2.4, p.4).
The paperâs procedure is concrete:
- Translate
Chain p q rintoChainTy p q r = x:a â y:b â z:c â {v:Bool | q x y â p y z â r x z}(§2.4, p.4). - Treat the bound function as an extra ghost parameter when checking the bounded function.
- Convert applications to administrative normal form and insert a ghost call after intermediate values are named. The paper shows
t1 = g x,t2 = f t1, then_ = $chain x t1 t2(§2.4, p.4). - At a caller, create a lambda returning
true, such ascompose (λ_ _ _ â true) incr incr; function subtyping checks that this lambda satisfiesChainTy(§2.4, p.4).
The resulting verification condition is quantifier-free and is discharged by SMT. The paper notes a cost bound: materialization can insert up to n^k calls, where k is the number of bound parameters and n the number of in-scope variables, although it says the practical number is small in compose (§2.4, p.4).
Inference: Verus can use a ghost proof fn or lemma with requires equal to the bound and ensures equal to its instantiated consequence. Call the lemma after naming intermediate values; do not make a proof fn part of executable code. Inference: this is a proof-encoding pattern, not a Rust type feature, and the lambda-returning-true trick must become an explicitly proved lemma rather than an unchecked assumption.
technique 3: bounded specifications for higher-order combinators
The paper demonstrates three reusable patterns. For composition, Chain relates input/output pairs of two functions: if q x y and p y z, then r x z. It instantiates p,q with relations of the form v == x + 1 and r with v == x + 2, proving the composed increment example (§2.3, p.3). For filtering, a Witness p w bound connects a Boolean test to an arbitrary refinement: âwhich says that for any boolean b such that w x b holds, if b is True then p x also holdsâ (§2.3, p.3). This proves a positive-only list from an isPos test without a special latent-predicate construct. For folding, Inductive inv step states that an invariant over the accumulator is preserved by each list element; instantiating inv with v == len xs and step with b' == b+1 verifies a length-counting fold (§2.3, pp.3â4).
How to use these patterns:
- Choose an abstract relation for each function boundary.
- State the closure, witness, or induction obligation as a bound.
- Instantiate the abstract refinements from the concrete call.
- Let constraint generation and abstract interpretation solve the resulting Horn constraints.
Inference: Verus equivalents are a spec fn relation over Seq or scalar values, a proof fn establishing composition, and a loop invariant for the fold case. Verus does not infer these relations automatically, so an agent must propose them and Verus must check every obligation. Inference: for a Rust iterator, preserve the paperâs logical relation but replace Haskellâs higher-order type with explicit closure contracts or a ghost model of the sequence.
technique 4: infer instantiations with monotonic abstract interpretation
The implementation generates templates with fresh refinement variables, emits subtyping constraints, and solves them with abstract interpretation borrowed from Liquid Typing (§3.6, p.8). The paper states: âInference Requires Monotonic Constraints.â It therefore prohibits implications outside bounds, pushes antecedents into the environment, and keeps unknown predicates in positive positions. This is what makes call-site instantiation automatic while retaining quantifier-free SMT obligations.
Inference: Verus has no Liquid inference pass. A product can imitate the workflow by generating candidate spec fns and requires/ensures, then asking Verus for counterexamples, but this is an agent-side synthesis loop rather than a Verus feature. The assumption that candidate predicates are monotonic must be checked by the synthesis tool; Verus alone will not infer a missing higher-order relation.
technique 5: typed relational algebra and database schemas
The paper encodes rows as dictionaries whose keys and values carry refinements, then defines refined tables and operators. A schema refinement fixes the domain and value type for each key; the movie example rejects a year below 1888 or a misspelled Dir key (§4.1, pp.8â9). Operators preserve schemas: union and diff return the same schema, select constrains predicates to available fields, and project changes the domain to exactly the selected fields (§4.2, p.9). The paper emphasizes that âAll schema reasoning happens at the granularity of the logical refinementsâ (§4.2, p.9), and erasing refinements leaves well-typed Haskell but loses safety for operations such as dynamic lookup.
Inference: in Verus, model a row with a ghost Map<String, Univ> or a fixed Rust struct, and use spec fn predicates for key sets and value tags. Use requires for schema membership and ensures to preserve or restrict the key set. The paperâs dynamic dictionary operations do not carry over directly to Rust ownership; a fixed struct or a ghost map plus explicit bounds is safer.
technique 6: RIO state transformer, FloydâHoare combinators, and capability framing
RIO indexes a stateful computation by a precondition over World and a two-state postcondition. The paper then encodes mutable variables with McCarthy select/update, whose quantifier-free theory SMT solvers decide (§5.2, p.11). ifM and whileM receive bounds corresponding to FloydâHoare premises: loop invariance, exit implication, and a one-state postcondition. The worked whileTest decrements a nonnegative counter and verifies that it ends at zero (§5.2, pp.11â12).
The capability extension maps file handles to privilege sets. API contracts distinguish privilege-preserving operations (eqP) from allocation and privilege derivation (alloc, copyP, derivP). Recursive copyRec initially loses facts about source and destination; the paper introduces a Stable i bound and frames i through the recursion, then verifies the client with the minimal listed privileges (§6.1â6.3, pp.12â13).
Inference: Verus can represent World as a ghost state model, use requires/ensures for pre/post relations, and place invariant clauses on loops. Use old(...) in postconditions for pre-state values, and prove frame properties with lemmas over unchanged map entries. Inference: capability tokens can be represented by ghost sets or linear ghost state, but the paperâs Haskell dictionary semantics and Shill runtime assumptions require a Rust-specific API model. Inference: this technique is directly relevant to VL when verifying resource access, because it makes permissions and non-revocation explicit; it does not by itself prove the real filesystem or OS behavior.
what is proved, measured, and trusted
The paper proves semantic preservation and type soundness of λB and demonstrates checked libraries in Liquid Haskell. It reports no corpus size, success fraction, timing, or comparison denominator. The examples are evidence that the encoding works, not evidence of industrial scale. The trusted base includes the λP soundness result, SMT solver correctness, Liquid Haskell implementation, primitive state/file-system model, and the userâs refinements. Inference: a wrong or overly strong refinement can produce a valid proof of the wrong property; erasing refinements preserves host-language typing but removes the guarantees. Inference: for agent-written verified Rust, the threat is high for specification omission and model mismatch, medium for proof-generation errors if Verus checks all generated obligations, and concentrated in external APIs, ghost models, and inferred contracts.
No earlier study in the local walkthrough has this paperâs specific bounded-refinement encoding; its closest reusable Verus ideas are explicit contracts, loop invariants, ghost state, and proof lemmas. VL should not redo the paperâs abstract interpretation or dictionary/RIO encodings unchanged; it should adapt the closure, witness, induction, and framing patterns to Rust APIs and measure whether generated proofs remain stable under dependency and model changes.
techniques for the catalog
- bounded refinement bounds â constrain abstract predicates with logical relations so higher-order specifications remain modular
- ghost-function elaboration â translate each bound into a first-order ghost function and materialize it at use sites
- closure, witness, and induction bounds â specify composition, filtering, and folds through reusable relational obligations
- monotonic refinement inference â synthesize call-site predicates by solving positive Horn constraints with abstract interpretation
- schema-refined relational algebra â preserve database key and value invariants through typed dictionary and table operators
- RIO FloydâHoare indexing â encode stateful pre/postconditions, branches, and loops as bounded refinements
- capability framing â track resource privileges and preserve stable facts across recursive state transformations Checked 2026-10-14: audit record vazou2015bounded; paper-study comparison complete; gate executed; experiments not rerun
Last edited: