Skip to content

Allow --verify-function more than once - #68

Open
kiranandcode wants to merge 8 commits into
mainfrom
kg/repeat-verify-function
Open

kiranandcode wants to merge 8 commits into
mainfrom
kg/repeat-verify-function

Conversation

@kiranandcode

@kiranandcode kiranandcode commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

--verify-function can now be given more than once, and used with several modules.

Semantics

  • Each pattern selects functions: exact name, unique substring, or * wildcards at the ends. The run verifies the union of the selections, and each function is checked once.
  • Every pattern is resolved before the run fails. The messages for all bad patterns go into one error, separated by a blank line. A repeated pattern is resolved and reported once. With one bad pattern the output is byte-identical to a single flag.
  • --verify-function still needs --verify-only-module or --verify-root, and still rejects --verify-module. --verify-only-module can be repeated, and --verify-root can be added alongside. A repeated module counts once.

One rule

Every pattern goes through one matcher, whether one module or several are selected.

  • Names: every pattern is matched against each function's name relative to its module (as before). A pattern with :: is also matched against:

    • the function's path from the crate root, with or without crate:: (first::shared, crate::first::shared, crate::beta for the root);
    • its module's path followed by its relative name (with the crate's name dropped), with or without crate::. For impl crate::S<u8> { fn f } written in module ta, that is ta::S::f. So methods of impls of one type in two modules can be told apart.
  • Wildcards: a pattern with * at either end selects every function with a name it matches, by any of these names.

  • Exact patterns (no * at either end) go through one precedence step, the same with one module or several:

    1. an exact match on the relative name;
    2. if none, an exact match on a qualified name (the crate-root or module-path readings);
    3. if none, the substring fallback: a unique substring match is selected, and several are an error.

    All matches at the first step that finds any are selected if they are in one module (e.g. G::g selects both G<u8>::g and G<u16>::g, and so does ninth::G::g). Matches in more than one module are an ambiguity error. So line::f with module line is the method f of line::line (its relative name), as on main, and not also f in module line (its path). crate::line::f and line::line::f each select one.

  • Hints follow the same rule:

    • The ambiguity hint names one matched function by a name that the resolver selects only that function by (the listed name, then the qualified names, in order).
    • If no name selects only one function, the hint names the functions of one module: e.g. eleventh::ninth::G::g when ninth and eleventh each have two methods ninth::G::g.
    • The old fallback {pattern}* is gone, since it also selected other functions starting with the pattern. If no name selects even one module's matches, the error says so and gives no hint.
    • The "more than one match" hint *pat* and the "could not find" hint *clean* match by the same names, so they select exactly the functions listed.
  • Listings with several modules show each function's path from the crate root (crate:: plus the name for the root). When several listed functions share a name, each gets its location: - S::f (at src/lib.rs:98:31). With one module, listings are unchanged.

Methods of impls for a type of another crate (e.g. impl Tw for Seq<int> in module twelfth) have no path from the crate root, because their name is the other crate's path (vstd::seq::Seq::tw). They can still be qualified: by the module's path followed by that name, twelfth::vstd::seq::Seq::tw (or crate::twelfth::vstd::seq::Seq::tw). That spelling is not a real Rust path, but it is unique to the module.

Compared with main

Patterns without ::, with one module: byte-identical to main, since they match only the relative name, and with one module there is no ambiguity error and no location in listings.

Patterns with :: that work on main and change, from the comparison below (20 of 318 working inputs). Exact patterns that main matches exactly by relative name are unchanged.:

  • An exact pattern that main resolved as an accidental unique substring:
    • a::f in module a: main selects Data::f (its name contains a::f); now selects f, whose path is a::f.
    • :: in first, a and line, and ::f in a and line: main selects the one relative name containing :: (e.g. Data::f). Every path contains ::, so these are now "more than one match" errors.
  • A wildcard pattern that now also matches paths, so it selects more: *a::f, *a::f*, *a::*, *a::**, *::f, *::f* in a; *::* in first, a and line; *::f, *::f*, *line::f, line::f*, *line::f* in line.

Changes

  • config.rs: help text for PATTERN. The "at most one --verify-only-module or --verify-root" check is gone.
  • user_filter.rs:
    • UserFilter::Function(Vec<ModuleId>, HashSet<Fun>).
    • Each selected function is named once. FunName holds the module, the relative name, the qualified names, the listed name and the location. The relative name and the path are cut from the function's full name. Each module's prefixes are computed once, so the friendly-name map's lock is taken once per function (plus once per module).
    • resolve is the single rule. It returns a Resolution, and message words the error.
  • driver.rs: is_verifying_entire_crate checks is_empty().
  • Not changed: is_verifying_entire_crate ignores --verify-only-module (already so on main), so a run with only --verify-only-module prints no "(partial verification …)" note.

Testing

rust_verify_test/tests/verify_function.rs has 29 tests, all passing. They run through common::run_verus_raw and compare error messages line by line. They cover:

  • union, overlap, failing functions, every bad pattern reported, a repeated bad pattern reported once;
  • two modules, root plus a module, wildcards across modules, *shar* hints that work;
  • qualified patterns with one module (first::shared, first::only_*, crate::beta, crate::alph hinting *crate::alph*, which verifies 2);
  • a::f selects f with a alone and with a and first, and so does crate::a::f; a::Data::f selects Data::f;
  • cell::f across cell, fifth and sixth is the relative name of the methods in fifth and sixth, so it is ambiguous between those two and hints fifth::cell::f; crate::cell::f selects f in cell;
  • line::f selects only the method line::line::f (its relative name wins over the path of f), with line alone and with line and first; crate::line::f and line::line::f each select one;
  • with first, second and eighth (struct first with method shared), shared hints crate::first::shared, not first::shared;
  • G::g, ninth::G::g and crate::ninth::G::g each verify both methods, with ninth alone and with ninth and first;
  • methods of crate::S in ta and tb: S::f is ambiguous, hints ta::S::f, and lists both with their locations; ta::S::f and crate::ta::S::f select one;
  • ninth::G::g with ninth and eleventh (two methods each): the hint names one module's functions (eleventh::ninth::G::g), which verifies 2;
  • twelfth::vstd::seq::Seq::tw names a method of an impl for vstd's Seq;
  • moved (in first, of second::Item) is listed as second::Item::moved and matched by crate::second::Item::moved, second::Item::moved, its full name and first::second::Item::moved, with one or two modules.

Also:

  • Other suites: module_order (1), report_json (11) and resident (70) pass with VERUS_CVC5_PATH set to an absolute path. vargo fmt -- --check is clean. I did not run the full rust_verify_test suite.
  • Comparison with main: a release binary built from origin/main (3fa79fdb) against this branch, on 4720 single-module, single-pattern invocations of the test fixture: 16 module selections × 295 patterns (exact, substring, ambiguous, no-match, each with * at either or both ends, module- and crate::-qualified, type-qualified, interior *, ::, *). Compared stdout, stderr and the exit code.
    • 318 inputs work on main: 298 are byte-identical, and the 20 that differ are the :: patterns listed above.
    • Of the inputs that fail on main, 266 now give different output, and every one of them contains ::.

Earlier real check (first commit, one module)

On verified-nrkernel impl_u::os_refinement (pristine), with the os-bench flags (-V cvc5 --rlimit 10 --num-threads 4 --multiple-errors 50):

run result wall
step_MapEnd_refines 0 verified, 1 error 28 s
lemma_map_soundness_equality 0 verified, 1 error 33 s
both, one process 0 verified, 2 errors, same two error locations 59 s
lemma_protect_soundness_equality 1 verified, 0 errors 9 s
step_MapEnd_refines + lemma_protect_soundness_equality 1 verified, 1 error 39 s
main binary, both flags Option 'verify-function' given more than once, exit 255 —

The saving per extra function is one front end. The box was shared, so the timings are only indicative. I have not re-run nrkernel with several modules.

Why: the os-bench verus wrapper re-runs fork verus on the changed functions that fail, for --expand-errors. With this change it can do that in one process, even when the functions are in different modules, and it can always pass qualified names.

🤖 Generated with Claude Code

kiranandcode and others added 5 commits October 7, 2026 21:12
Each pattern selects functions as before, with its own no-match and
ambiguity errors; the run verifies the union. A single flag behaves as
before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every pattern is resolved before failing; the no-match and ambiguity
messages of all bad patterns go into one error, each worded as before.

--verify-function now accepts several --verify-only-module (and
--verify-root alongside them). Each pattern is matched across all of
them; a pattern that matches in two modules is an error unless it is
qualified by its module (foo::bar::f, or crate::f for the root).
Qualifiers are only recognised with more than one module, so a single
module behaves exactly as before.

UserFilter::Function is now (Vec<ModuleId>, HashSet<Fun>), without the
unused pattern list, and each module's function names are built once.
The help text names the value PATTERN and describes the union.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A pattern is first matched as written; only if that fails is a leading
  module name (`m::`, `crate::m::`, or `crate::` for the root) taken as a
  qualifier, longest first. This works with one selected module too, so a
  caller can always qualify. Inputs that work on main are unchanged.
- An unqualified name such as `T::f` (a method of `T` in another module) is
  no longer read as `f` in a selected module `T`.
- Wildcard matches in several modules are unioned; only an exact name that
  matches in two modules is an error. The "consider *pat*" hints now work.
- Share module-name formatting with `verifier::module_name`.
- Tests run through `run_verus_raw` (binary suffix, solver paths) and
  compare the joined error without depending on rustc's indentation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A qualified pattern was matched as written first, and read as qualified
only if that failed. So `a::f` could select `Data::f` (a unique substring
match) over `f` in module `a`, and an ambiguity across modules could be
resolved silently by the qualified reading.

- With several modules, each function is matched by the pattern as written
  and by the pattern without its own module qualifier, in the same step:
  exact (or wildcard) matches first, then unique substring matches. An
  exact name that matches in two modules, by any reading, is an error;
  its hint names `crate::m::f` when `m::f` is the pattern itself.
- With one module, the pattern is first matched only as written, exactly
  as on main, so every pattern that works on main selects the same
  functions with byte-identical output; qualified readings apply only
  when that fails.
- Qualifiers come from the module's friendly name (the one names are made
  relative to), with the crate name written `crate`; a name that is not
  relative to its module is shown and matched as is, not prefixed.
- Wildcard hints list one wildcard per reading that matched.
- The four error listings share one helper.
- The help text no longer stops mid-sentence.
- The two-module ambiguity test compares lines without rustc's indentation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kiranandcode and others added 3 commits October 8, 2026 07:18
With one module, every pattern selects what it selects on main. Otherwise
(several modules, or a pattern with `::` that main cannot resolve), a pattern
with `::` is also matched against each function's path from the crate root,
with or without `crate::`, and a pattern without `*` at its ends must select
exactly one function, whether or not the matches share a module. The
ambiguity hint suggests a name that the same rule resolves to one function,
and the wildcard hints match by the same names, so they select what is listed.

A function named by a path outside its module (`impl crate::second::Item`
inside `first`) can now be named `crate::second::Item::moved`. A pattern is
exact when it has no `*` at either end, as on main. A repeated pattern is
resolved (and reported) once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every pattern goes through one rule. A pattern is matched against each
function's name relative to its module, and, with `::`, also against its
path from the crate root and its module's path followed by its relative
name, each with or without `crate::`. Exact or wildcard matches come first,
then a unique substring. Exact matches in one module are all selected;
exact matches in several modules are ambiguous.

- Methods of impls of one type in two modules can be told apart by module
  (`ta::S::f`).
- The ambiguity hint names one function, or else the functions of one
  module, and no longer suggests a wildcard that selects more.
- Listings with several modules show where same-named functions are defined.
- Comments and tests describe the rule rather than main.
- Function names are derived from the full name once, with each module's
  prefixes computed once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An exact pattern is matched against relative names first, then against
the qualified names (path from the crate root, module path), then by
unique substring. So `line::f` with module `line` selects the method
`line::line::f`, as on main, rather than also `f` in `line`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant