Skip to content

Add extern_spec for NonZero - #2220

Open
Kixunil wants to merge 3 commits into
creusot-rs:masterfrom
Kixunil:spec-for-nonzero
Open

Add extern_spec for NonZero#2220
Kixunil wants to merge 3 commits into
creusot-rs:masterfrom
Kixunil:spec-for-nonzero

Conversation

@Kixunil

@Kixunil Kixunil commented Aug 4, 2026

Copy link
Copy Markdown

NonZero is a standard library type that is used for layout optimizations as well as using the type system to prove basic invariants. So far creusot didn't support it.

This adds basic support for the type, so far only adding View, DeepModel and Invariant impls as well as constructors and the get method.

Closes #2217

@jhjourdan jhjourdan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! Could you please address the comments before we merge?

Comment thread creusot-std/src/std/num.rs Outdated
Comment on lines +611 to +617
extern_spec! {
mod core {
mod num {
trait ZeroablePrimitive: View<ViewTy = Int> + DeepModel<DeepModelTy = Int> {}
}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a no-op. Extern specs cannot be used to add a super trait to a trait. Could you please remove this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sure, I thought it might be unneeded and forgot.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then could you remove this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the newest push, I forgot to commit it. 🙈

Comment thread creusot-std/src/std/num.rs Outdated
Comment thread creusot-std/src/std/num.rs Outdated
@jhjourdan

Copy link
Copy Markdown
Collaborator

Also you need to fix formatting. Please run ./fmt.

@jhjourdan

Copy link
Copy Markdown
Collaborator

I thought a bit more about this, and I think it should be useful (e.g., for bitwise operations) to have an opaque logical function from NonZero<T> to T and then to specify all the other functions (view, deep model, program functions) in terms of that. Could you do that? Thanks!

`NonZero` is a standard library type that is used for layout
optimizations as well as using the type system to prove basic
invariants. So far creusot didn't support it.

This adds basic support for the type, so far only adding `View`,
`DeepModel` and `Invariant` impls as well as constructors and the `get`
method.
@Kixunil
Kixunil force-pushed the spec-for-nonzero branch 4 times, most recently from 2686b18 to ae8ebd8 Compare August 5, 2026 11:11
Using an opaque logic function was requested as it should be more
useful.
@Kixunil

Kixunil commented Aug 5, 2026

Copy link
Copy Markdown
Author

Is this what you had in mind? I also remembered that the code could be check(ghost) but it seems there's some problem with it that I can't understand. I'm still a beginner when it comes to creusot/formal verification in general.

Comment on lines +628 to +637
#[cfg(creusot)]
impl<T: ZeroablePrimitive> Plain for NonZero<T> {
#[trusted]
#[ensures(*result == *snap)]
#[check(ghost)]
#[allow(unused_variables)]
fn into_ghost(snap: Snapshot<Self>) -> Ghost<Self> {
Ghost::conjure()
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need that?

I am not very confortable with this instance, because it implies that there is no null value of NonZero at the logical level, which in turns implies that we cannot model NonZero<T> with T directly (if we do that, then we exclude 0 at the program/ghost level, but not at the logical level). Modeling NonZero<T> with T directly is a simple way of specifying that get_logic is injective (which is sometimes useful but tedious in general).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so users should just call get() in ghost code and use the resulting value for proofs? The reason I added this was that I came across the trait, saw it implemented on a bunch of primitives with the rationale that they are not pointers so since this isn't a pointer either I though it could be added for completeness. But I don't personally need this.

Also note that char has a niche and also impls Plain so maybe that one is also wrong?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is not with having a niche or not. The point is that to implement Plain, the logical meaning of the type should be the same as the meaning in programs. It's not the case for pointer types because they carry ownership in programs. It's neither the case here because of the niche, which is not enforced in the logic because type invariants are not enforced in the logic.

That being said, I think we should consider adding the type invariant as a precondition of into_ghost, but this is another story...

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, please remove this instance and I'll merge.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add extern_spec for NonZero<T>

2 participants