Fold invariant_context into context - #231
Conversation
f0652ce to
e1aa185Compare`#[thrust_macros::context]` stamped each method of an `impl`/`trait` with the
enclosing header, while `#[thrust_macros::invariant_context]` threaded the same
header -- plus the host signature -- into the `invariant!` calls in a function body.
Two attributes for one question: what does the code inside this item see?
They become one. `#[thrust_macros::context]` now takes a function as well, and an
`impl`/`trait` hands each of its methods the enclosing header along with the attribute
itself, so a method carrying a loop invariant no longer needs one of its own:
#[thrust_macros::context]
impl Counter {
fn run(&mut self) -> i64 {
while rand() {
thrust_macros::invariant!(|init: Self, self: &mut Self| ..);
}
}
}
The `impl`/`trait` expansion stays a distributor: it knows which items are methods and
what header they sit under, and nothing about what a body holds. Each method's body is
threaded by its own expansion of the attribute, the same one a free function gets,
which reads the header back from `#[thrust::_outer_context(..)]` -- the path
`requires`/`ensures` already take.
Threading a body that names no spec macro leaves it alone, rather than extending its
where clause with `Model` predicates nothing asked for: with every method of a
`#[context]` item threaded, those bounds would otherwise land on methods that have no
formula to justify them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJ6XNNsSBdPkAzrWHftvqVe1aa185 to
36f8ffeCompareThere was a problem hiding this comment.
Pull request overview
Unifies invariant context propagation under #[thrust_macros::context] for functions, impls, and traits.
Changes:
- Extends
contextto rewrite invariants and add required model bounds. - Automatically applies context propagation to impl and trait methods.
- Removes
invariant_contextand migrates UI tests.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
thrust-macros/src/context.rs | Implements unified context expansion. |
thrust-macros/src/invariant.rs | Updates context documentation. |
thrust-macros/src/invariant_context.rs | Removes superseded implementation. |
thrust-macros/src/lib.rs | Removes the old attribute and updates exports. |
thrust-macros/src/spec.rs | Adds mutable signature access. |
tests/ui/pass/loop_invariant_trait.rs | Tests automatic trait-method context. |
tests/ui/pass/loop_invariant_trait_self.rs | Tests Self invariants in traits. |
tests/ui/pass/loop_invariant_self.rs | Tests automatic impl-method context. |
tests/ui/pass/loop_invariant_self_receiver.rs | Tests receiver-based invariants. |
tests/ui/pass/loop_invariant_outer_param.rs | Migrates free-function context usage. |
tests/ui/pass/loop_invariant_generic.rs | Migrates generic invariant coverage. |
tests/ui/pass/loop_invariant_generic_closure.rs | Migrates generic closure coverage. |
tests/ui/pass/loop_invariant_fn_param_closure.rs | Migrates closure-parameter coverage. |
tests/ui/pass/loop_invariant_fn_param_at_entry.rs | Migrates entry-value coverage. |
tests/ui/fail/loop_invariant_trait.rs | Updates failing trait invariant test. |
tests/ui/fail/loop_invariant_trait_self.rs | Updates failing trait Self test. |
tests/ui/fail/loop_invariant_self.rs | Updates failing impl invariant test. |
tests/ui/fail/loop_invariant_self_receiver.rs | Updates failing receiver test. |
tests/ui/fail/loop_invariant_outer_param.rs | Updates failing outer-parameter test. |
tests/ui/fail/loop_invariant_generic.rs | Updates failing generic test. |
tests/ui/fail/loop_invariant_fn_param_closure.rs | Updates failing closure test. |
tests/ui/fail/loop_invariant_fn_param_at_entry.rs | Updates failing entry-value test. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:36f8ffe231
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
#[thrust_macros::context]stamped each method of animpl/traitwith the enclosing header, while#[thrust_macros::invariant_context]threaded the same header — plus the host signature — into theinvariant!calls in a function body. Two attributes for one question: what does the code inside this item see?They become one.
#[thrust_macros::context]now takes a function as well, and animpl/traithands each of its methods the enclosing header along with the attribute itself, so a method carrying a loop invariant no longer needs one of its own:A free function still opts in explicitly, with
#[thrust_macros::context]in place of the old name.Who does what
The
impl/traitexpansion stays a distributor: it knows which items are methods and what header they sit under, and nothing about what a body holds. It stamps two attributes per method —— and each method's body is then threaded by its own expansion of the attribute, the same one a free function gets, which reads the header back from
#[thrust::_outer_context(..)]: the pathrequires/ensuresalready take. That expansion accepts a free function, an impl method, or a trait method through the existingFnItemWithSignature, so a trait method with no body needs no special case.ContextInjectormoves over frominvariant_context.rsunchanged.Not extending a where clause nothing asked for
Threading a body that names no
invariant!now leaves it alone.invariant_contextextended the host's where clause with theModelpredicates unconditionally, which was fine while it was opt-in per function; with every method of a#[context]item threaded, those bounds would otherwise land on methods that have no formula to justify them.Changes
context.rsimpl/traithands its methods the header and this attribute.ContextInjectormoves here frominvariant_context.rs, which is deletedspec.rsFnItemWithSignature::sig_mut, to extend the threaded function's where clausetests/ui#[thrust_macros::context], methods drop the attribute entirelyNo behaviour change for the analyzer; the macro expansion each test produces is the same as before.