Add param/ret/sig refinement-type annotations via thrust_macros#98
Draft
coord-e wants to merge 8 commits into
Draft
Add param/ret/sig refinement-type annotations via thrust_macros#98coord-e wants to merge 8 commits into
coord-e wants to merge 8 commits into
Conversation
Introduce `thrust_macros::param`, `thrust_macros::ret`, and
`thrust_macros::sig` attribute macros that lower refinement types (e.g.
`{ v: i32 | v > 0 }`) into `#[thrust::formula_fn]`s, giving refinement
formulas the same rustc-typechecked treatment as requires/ensures. Each
refinement is placed via a new "type position" — a path addressing into
the function type (parameter index or return slot, then generic-argument
indices for enum args and Box pointees) — emitted as a
`#[thrust::refine(..)]` path statement and installed into the parameter
or return RefinedType template.
Migrate the existing `thrust::sig` tests to `thrust_macros::sig`.
Replace the bare `Vec<usize>` type position with `rty::TypePosition` (a `TypePositionRoot` of `Param(idx)` / `Return`, plus a projection of nested type-argument indices). The `#[thrust::refine(..)]` attribute now uses the `result` keyword to select the return instead of the parameter count, which was unintuitive. Adds `Display` (`$1`, `result.0`).
Replace the previous TypePositionRoot + projection: Vec<usize> split with a flat Vec<TypePositionStep>, where each step is one of: - Param(FunctionParamIdx): navigate into a function type's parameter - Return: navigate into a function type's return slot - TypeArg(usize): navigate into a generic type's type argument This makes the path representation uniform across all type levels, enabling future support for refinements on positions inside higher-order function types (e.g. [$0, result] for the return type of a function-typed first parameter). The attribute encoding changes accordingly: - result (ident) => Return - integer i => Param(i) - bracket group [i] => TypeArg(i) The macro-side RefineRoot+projection split is similarly replaced with a flat Vec<RefineStep> in the Refinement struct, and scan_type now threads the full steps Vec through recursive calls. https://claude.ai/code/session_01Km9xwaVaGQjnAy1iHsPewa
Align the macro-emitted attribute with the existing requires_path / ensures_path convention: the _path suffix conveys that the attribute's target is a path to a formula_fn. The symbol-path helper follows suit (refine_path -> refinement_path_path). https://claude.ai/code/session_01Km9xwaVaGQjnAy1iHsPewa
Move the param/ret/sig expansion logic and its token-scanning helpers out of the crate root into a dedicated refine module, leaving only the thin proc-macro entry points in lib.rs. Shrinks lib.rs from ~1232 to ~813 lines. https://claude.ai/code/session_01Km9xwaVaGQjnAy1iHsPewa
Encode function parameters as $i (matching FunctionParamIdx's Display) and type arguments as bare integers, instead of bare integers for params and bracketed [i] for type args. Reads more naturally and keeps the attribute syntax consistent with how parameters are displayed elsewhere. https://claude.ai/code/session_01Km9xwaVaGQjnAy1iHsPewa
- Rename FunctionTemplateTypeBuilder::refine to install_refinement_at, matching the RefinedType method it delegates to. - Rename extract_refine_paths / extract_refine_annots to extract_refinement_paths / extract_refinement_annots, and clarify the related panic messages, to reduce the overloaded use of "refine". - Make TypePosition's Display match the refinement_path(..) surface syntax (comma-separated steps) instead of dot-separated. - Replace the macro's build_refine_jobs (and its complex tuple return type) with annotated_type_exprs returning PositionedTypeExpr, and introduce NamedType / SigAnnotation structs so parse_sig_attr no longer needs an allow(clippy::type_complexity). Rename RefineStep to PositionStep, RefineKind to AnnotationKind, and expand_refine to expand for clarity. https://claude.ai/code/session_01Km9xwaVaGQjnAy1iHsPewa
- Replace the undefined phrase "refine position" in parser diagnostics with "type position". - Drop the non-empty invariant from TypePosition: an empty path is a valid notion (it addresses the type itself); a path is only non-empty when applied to a function type. TypePosition::new now takes the full step vector, and that non-emptiness is checked where it matters (the function-type builder). - Rename the macro module file thrust-macros/src/refine.rs to rty.rs and use TypePositionStep there, mirroring the plugin's rty module instead of naming the same concept differently. https://claude.ai/code/session_01Km9xwaVaGQjnAy1iHsPewa
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.
Summary
Adds
thrust_macros::param,thrust_macros::ret, andthrust_macros::sigattribute macros that express refinement types (e.g.List<{ v: i32 | v > 0 }>) and lower them to the same rustc-typechecked#[thrust::formula_fn]machinery already used byrequires/ensures. This gives refinement-type formulas full name resolution, model-type checking, user-defined predicates, etc. — instead of the limited hand-written DSL insrc/annot.rs.This is a step toward #70 (dropping the old annotation parser): it implements
thrust::param/thrust::ret(plussig) on top of the typechecked formula mechanism. See "Follow-up: removing the old parser" below.Design
A refinement is placed via a type position — a path of indices that addresses into the function type uniformly:
Boxpointee).Each macro emits, per refinement, a sibling
#[thrust::formula_fn]and injects a#[thrust::refine(i, j, …)]path statement into the body. The plugin collects these, translates each formula to aRefinement, and installs it into the parameter/returnRefinedTypetemplate via a newRefinedType::install_refinement_atnavigator.param(name: ty)— root[param_index(name)]ret(ty)— root[param_count]sig(fn(n0: t0, …) -> ret)— one job per arg + the return; the direct counterpart of the legacythrust::sigAll three share one token scanner and lower to the same
#[thrust::refine(..)]attribute + navigator (no param/ret special-casing in the plugin).Changes
thrust-macros/src/lib.rs:param/ret/sigproc macros + shallow type-expression scanner.src/analyze/annot.rs:refine_path()+ integer-position parser.src/analyze.rs: collect#[thrust::refine(..)]statements and resolve them to positioned refinements (reusingFormulaFn::to_ensure_annot).src/rty.rs:RefinedType::install_refinement_atnavigator (Function params/ret, enum args,Boxpointee).src/refine/template.rs:FunctionTemplateTypeBuilder::refineentry.src/analyze/local_def.rs: wiring.thrust::sigtests (annot_box_term) tothrust_macros::sig, plus newrefine_param_simple/refine_sigpass+fail tests.The legacy
parse_rtypath is left intact and untouched.Verification
cargo fmt --all -- --checkandcargo clippy -- -D warningsclean.Known limitation (pre-existing, not introduced here)
While building the type at any depth is correct, thrust's verifier reliably enforces only top-level (depth-0) parameter/return refinements today. Enum/datatype nested args are enforced soundly, but
Box(own-pointer) pointee refinements in return position are not — filed as #97. The committed tests therefore stick to depth-0 cases plus the whole-boxsigmigration.Follow-up: removing the old parser (#70)
After this change, nothing in the repo (src, tests, examples, injected
std.rsprelude) uses the legacythrust::param/thrust::ret/thrust::sigor rawthrust::requires(..)/thrust::ensures(..)forms — all usethrust_macros::*. TheAnnotParser(parse_rty/parse_formula) is reachable only from its own code paths and can be removed in a follow-up (retainingAnnotFormula, which the formula_fn path still uses). Left out of this PR to keep it focused.Test plan
cargo test(full UI + doc tests) passes with Z3 + COAR imagecargo fmt --all -- --checkcargo clippy -- -D warningsparam/ret/sigpass+fail tests behave correctly (refinement enforced)annot_box_termmigration: pass stays pass, fail stays UnsatRefs #70.
https://claude.ai/code/session_01Km9xwaVaGQjnAy1iHsPewa
Generated by Claude Code