Repository navigation
Resolve fn-pointer type arguments in contract target paths - #4916
Merged
Merged
Conversation
A Type::BareFn arm in resolve_ty builds the corresponding fn-pointer type (Rust ABI, non-variadic; lifetimes erased like every other path argument), so targets like <Wrap<fn(u8) -> u8> as Probe>::probe and the std adapters' <Map<Iter<u8>, fn(&u8) -> u8> as Iterator>::__iterator_get_unchecked resolve. A non-Rust-ABI or variadic fn pointer keeps the uninstantiated type, as before. (cherry picked from commit ba34bb1f568c4ce388e0e4175df2bc90720576f6)
Contributor
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The implementation is focused, consistent with existing lifetime handling, and covers supported and unsupported cases.
Review effort: Balanced
Findings: None
What changed in this PR
Adds contract-target resolution for Rust ABI function-pointer type arguments.
Changes:
- Constructs safe/unsafe, non-variadic Rust fn-pointer types recursively.
- Adds positive resolution tests and preserves unsupported C ABI behavior.
| File | Description |
|---|---|
kani-compiler/src/kani_middle/resolve/type_resolution.rs |
Resolves supported bare function types. |
tests/kani/FunctionContracts/generic_fn_pointer_argument.rs |
Tests plain, reference, and unsafe pointers. |
tests/expected/function-contract/generic_fn_pointer_cabi.rs |
Tests unsupported C ABI resolution. |
tests/expected/function-contract/generic_fn_pointer_cabi.expected |
Records the expected diagnostic. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
tautschnig
approved these changes
Sep 30, 2026
Erased regions make a higher-ranked fn-pointer target resolve to a coexisting 'static sibling impl. Corrects the comments that claimed the erased argument always matches, and adds h_hr as a fixme test. Binding the late-bound regions is tracked in model-checking#4933. Addresses review feedback on model-checking#4916 (Resolves model-checking#4909).
feliperodri
approved these changes
Sep 30, 2026
Merged
via the queue into
model-checking:main
with commit Sep 30, 2026
30242b2
21 of 22 checks passed
1 of 2 tasks
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.
A contract target whose type arguments include a fn-pointer type (
<Wrap<fn(u8) -> u8> as Probe>::probe, or the standard adapters'<std::iter::Map<std::slice::Iter<u8>, fn(&u8) -> u8> as Iterator>::__iterator_get_unchecked) failed to resolve:resolve_ty'sType::BareFnarm returnedunsupported, so the fn-pointer argument was dropped and the concrete impl could not match. This PR builds the fn-pointer type in that arm (Rust ABI, non-variadic; inputs and output resolved recursively; lifetimes erased like every other path argument; safety carried through), so these targets resolve.Verification
-Zfunction-contracts)tests/kani/FunctionContracts/generic_fn_pointer_argument.rsfn(u8) -> u8); a reference-carrying fn pointer (fn(&u8) -> u8) whose impl type is late-bound (for<'a> fn(&'a u8) -> u8), where the erased argument still matches; and anunsafefn pointer (safety carried through the built signature). The three impls have distinct postconditions, so each harness verifies only if resolution picks its own fn-pointer instantiation.tests/expected/function-contract/generic_fn_pointer_cabi.rsextern "C" fn(u8) -> u8) is not built by the arm and keeps the uninstantiated type.Confirmed locally:
generic_fn_pointer_argumentverifies 3/3;generic_fn_pointer_cabifails to resolve (the expected diagnostic); the resolver suites are unchanged (generic_argument_instantiation6,cross_module_multiple_impls7,multiple_inherent_impls3). The std target<std::iter::Map<std::slice::Iter<u8>, fn(&u8) -> u8> as std::iter::Iterator>::__iterator_get_uncheckedresolves, failing only on the absent contract (__iterator_get_uncheckedhas no contract). CI runs the full suite on the pinned toolchain.Mechanism: fn-pointer types were the last commonly-written argument shape
resolve_tyrejected outright. Building the signature (Rust ABI only, non-variadic, arguments resolved through the sameresolve_tyrecursion, lifetimes erased,unsafesafety carried) makes the argument concrete, and the existing full resolution then matches the impl. Anything the arm does not build (a non-Rust ABI, or a variadic fn pointer) keeps the old uninstantiated type, so nothing that resolved before changes.Resolves #4909. That issue reports this wall for the standard iterator adapters:
MapandFiltercarry a mapping or predicate function argument whose fn-pointer instantiation could not be named. The Challenge 16 submission (model-checking/verify-rust-std#549) falls back to akani::assumemirror harness forMap's__iterator_get_uncheckedfor this reason; with the arm,Map<Iter<u8>, fn(&u8) -> u8>resolves as aproof_for_contracttarget. As #4909 notes, closure instantiations stay out of reach for any resolver change (a closure's type cannot be written in path syntax), so only the fn-pointer instantiations are addressed here.BareFnis also one of the two argument kinds #4830 names as prerequisites for the semantic-resolution rewrite; it lands here on its own, ahead of that refactor.Scope: lifetimes are erased, so a higher-ranked fn pointer resolves against an erased signature; if a
fn(&'static u8) -> u8sibling impl also exists, the higher-ranked target resolves to it instead. Thegeneric_fn_pointer_higher_ranked.rsfixme test documents this, and binding the late-bound regions is tracked as a follow-up (#4933).By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.