镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

A Ghost<T> struct field cannot be used as its contents in ensures/ghost! (E0599 no method push/len, E0369 for ==), while a Ghost<T> parameter can #300

Open

Description

@coeff-aij

Summary

std.rs documents Ghost<T> as "in the logic it is its content, so a specification refers to a Ghost<T> as if it were a T". That holds for a Ghost<T> parameter: a lifted formula function lowers each parameter type to <T as Model>::Ty, and Ghost<T>'s Model impl resolves to the content's model. It does not hold for a Ghost<T> struct field. When the struct's Model::Ty is the struct itself, a formula that projects the field sees Ghost<Seq<Int>>. Ghost<T> only implements Clone, Copy and Model, so push, len and == on the field fail to type-check (E0599, E0369), in ensures and in ghost! bodies alike.

As a result, a ghost history cannot live in the struct it describes and be named by field. It has to be passed through a free function taking a Ghost<T> parameter, or the struct's Model::Ty has to be rewritten as a tuple and the field reached by position (as tests/ui/pass/ghost_field.rs does).

Minimal reproduction

len_only.rs:

use thrust_models::model::{Int, Seq};
use thrust_models::{Ghost, Model};

struct Hist {
    produced: Ghost<Seq<Int>>,
}
impl Model for Hist {
    type Ty = Hist;
}

#[thrust_macros::ensures((*h).produced.len() >= 0)]
fn bump(h: &mut Hist, x: i64) {
    let _ = x;
}

fn main() {
    let mut h = Hist {
        produced: thrust_macros::ghost!(|| -> Seq<Int> { Seq::empty() }),
    };
    bump(&mut h, 1);
}

On main e112206:

$ export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
$ THRUST_SOLVER=true target/debug/thrust-rustc -Adead_code -Aunused -C debug-assertions=false --edition 2021 len_only.rs
error[E0599]: no method named `len` found for struct `thrust_models::Ghost<T>` in the current scope
   --> len_only.rs:11:40
    |
 11 | #[thrust_macros::ensures((*h).produced.len() >= 0)]
    |                                        ^^^ method not found in `thrust_models::Ghost<thrust_models::model::Seq<thrust_models::model::Int>>`

Replacing the ensures with #[thrust_macros::ensures((!h).produced == (*h).produced)]:

error[E0369]: binary operation `==` cannot be applied to type `thrust_models::Ghost<thrust_models::model::Seq<thrust_models::model::Int>>`
   --> eq_only.rs:11:40
    |
 11 | #[thrust_macros::ensures((!h).produced == (*h).produced)]
    |                          ------------- ^^ ------------- thrust_models::Ghost<thrust_models::model::Seq<thrust_models::model::Int>>

The same happens inside a ghost! body that reads the field, e.g. ghost!(|h: &mut Hist, x: i64| -> Seq<Int> { (*h).produced.push(x) }) (E0599, no method push).

The control passes the frontend. It is the same history passed as a Ghost<T> parameter of a free function, and THRUST_SOLVER=true gets past rustc's type check and analysis to the solver stage:

#[thrust_macros::ensures(result == produced.push(x))]
fn push_produced(produced: Ghost<Seq<Int>>, x: i64) -> Ghost<Seq<Int>> {
    thrust_macros::ghost!(|produced: Ghost<Seq<Int>>, x: i64| -> Seq<Int> { produced.push(x) })
}

fn bump(h: &mut Hist, x: i64) {
    h.produced = push_produced(h.produced, x);
}

Seven variants (field in ensures, field in ghost!, a local rebound from the field, the parameter control) with their transcripts: https://github.com/coeff-aij/thrust/tree/71e759d41c1dd90daa8d712a976487d5e0beefc4/repro/ghost-field-contents. On e112206 every field variant fails with E0599/E0369 as above. The two controls, ctrl_param.rs and ctrl_local.rs (which rebinds the field to a local first), pass the frontend.

Expected

A Ghost<T> field used in a specification is typed as its content, as a Ghost<T> parameter is.

Notes

The refinement side already builds a Ghost<T> field at the content's sort. Only the surface typing of the formula is missing. A fork has one possible fix in coeff-aij@4fc9b1a. It gives Ghost<T> a Deref to <T as Model>::Ty and a PartialEq against any type with the same model, both #[thrust::ignored] stand-ins in std.rs, plus the matching arm in the annotation lowering. It panics if executable code (not a specification) calls something that relies on them.

I checked the tracker for "Ghost"/"ghost" and found no existing issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions