Fix E0401 for invariant_context on trait methods#130
Conversation
00df0d4 to
a3b4c0c
Compare
a3b4c0c to
21770e6
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes a Rust E0401 compilation failure when #[thrust_macros::invariant_context] is applied to trait methods containing thrust_macros::invariant!(...), by ensuring Self is re-bound when it can leak into the generated nested #[thrust::formula_fn] via propagated where predicates (not only via the closure body).
Changes:
- Treat
Selfas “used” if it appears in propagatedwhere-clause predicates (def_wheres), not just in the invariant closure tokens. - Ensure trait-method invariants properly rewrite/bind
Selfeven when the closure does not mentionSelfdirectly. - Add UI tests covering both satisfiable and unsatisfiable loop-invariant cases in a trait method context.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
thrust-macros/src/invariant.rs |
Expands the Self-usage detection to include propagated where predicates so nested formula fns don’t see unbound Self. |
tests/ui/pass/loop_invariant_trait.rs |
Adds a passing UI test for invariants in a trait method where Self only reaches generated bounds indirectly. |
tests/ui/fail/loop_invariant_trait.rs |
Adds a failing (Unsat) UI test for the same trait-method scenario to validate solver/reporting behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 21770e6a4b
ℹ️ 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".
| let self_used = crate::tokens_contain_ident(&closure.to_token_stream(), "Self") | ||
| || def_wheres | ||
| .iter() | ||
| .any(|pred| crate::tokens_contain_ident(&pred.to_token_stream(), "Self")); |
There was a problem hiding this comment.
Rewrite Self in copied generic parameter bounds
This detection still ignores Self that is copied through def_params, so trait methods with generic parameter bounds such as fn run<T: Marker<Self>>(&mut self) still expand to a nested formula function like fn _thrust_invariant_0<T: Marker<Self>, __ThrustSelf>(...), which triggers the same E0401 this change is trying to avoid. The branch rewrites parameters, the body, and def_wheres, but it never rewrites the generic parameter bounds already pushed into def_params; include those in the rewrite or move their bounds into the where-clause before rewriting.
Useful? React with 👍 / 👎.
#[thrust_macros::invariant_context]attached to a trait method fails to compile with E0401 ("can't useSelffrom outer item"):Root cause
invariant_contextrewrites each innerinvariant!into a free-standing#[thrust::formula_fn]nested inside the method body. Inside that nested item,Selfis out of scope, so anySelfreaching it is rejected with E0401.The macro already re-binds
Selfto a synthetic generic (__ThrustSelf, instantiated with the realSelfvia turbofish) — but it only did so when the closure mentionedSelf. On a trait method,Selfalso reaches the generated function'swhere-clause through two paths that were never rewritten:where Self: Sized), andSelf: Model/<Self as Model>::Ty: PartialEqbounds atraitouter context contributes throughmodel_where_predicates().So for
|x: i64| x >= 0(noSelfin the closure) those predicate-borneSelfs leaked through unbound. (The trait +Self-in-closure case was broken the same way.)https://claude.ai/code/session_01UvhtewjXmse6vpHq7BJ81S
Generated by Claude Code