Introduce ghost variables - #220
Draft
coord-e wants to merge 2 commits into
Draft
Conversation
A ghost variable is proof-only data: it has no runtime representation, and
program code cannot observe its content, but a specification refers to it as
if it were the value it stands for. `thrust_macros::ghost!` introduces one
from a logical term over the live variables the term names:
let s = thrust_macros::ghost!(|x: i64| -> Seq<Int> { Seq::singleton(x) });
The term expands into a formula function laid out like an `ensures` one --
parameter `0` is the introduced value, the rest are the named variables -- so
it reads as the return refinement of a function over those variables, and the
introduction as a call to that function. `Ghost<T>` has `T`'s model, so ghost
values pass through struct fields and function boundaries with the machinery
that already exists for any other value.
Disable the `RemoveZsts` MIR pass along the way. It rewrites reads of
zero-sized locals into constants, which drops the refinement of every value
whose type carries no runtime data. That covers `Ghost<T>` and the model
types alike: until now nothing constructed a model-typed value in program
code, so the limitation had no way to show up.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014jTCnjoii4e5r4VLEU733b
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014jTCnjoii4e5r4VLEU733b
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A ghost variable is proof-only data: it has no runtime representation, and program code cannot observe its content, but a specification refers to it as if it were the value it stands for.
thrust_macros::ghost!introduces one from a logical term over the live variables the term names:How it works
The term expands into a
#[thrust::formula_fn]laid out like anensuresone — parameter0is the introduced value, the rest are the named variables — so it reads as the return refinement of a function over those variables, and the introduction as a call to that function. The analyzer intercepts the marker call and feeds that function type to the existingrelate_fn_sub_type.Ghost<T>hasT's model, so ghost values flow through struct fields and function boundaries on the machinery that already exists for any other value; nothing along those paths needed changing.The parameters name live variables with their types, the same convention
invariant!uses. The return type names the logical type of the introduced value.Disabling
RemoveZstsThis MIR pass rewrites reads of zero-sized locals into constants, which drops the refinement of every value whose type carries no runtime data. Without disabling it, passing a ghost value to a function arrives as
const Ghost(PhantomData)and the binding is lost:This is not specific to ghost: it covers the model types (
Seq,Int, …) equally. Until now nothing constructed a model-typed value in program code — they only ever appeared as parameters — so the limitation had no way to show up.Tests
ghost_localghost_field&mut, tied to a real field by pre/postconditionsEach as a
pass/failpair.Known gaps
-A unused-variables.annot_struct_impl.rs), not something ghost introduces. GivingGhost<T>aDerefimpl plus an identity case inannot_fnwould lettype Ty = Selfkeep named fields.ghost!inside a genericimplneeds the context threadinginvariant_contextdoes for invariants. Not implemented here.🤖 Generated with Claude Code
https://claude.ai/code/session_014jTCnjoii4e5r4VLEU733b
Generated by Claude Code