Repository navigation
Conversation
This was referenced Sep 25, 2026
kasimte
added a commit
to kasimte/kani
that referenced
this pull request
Sep 30, 2026
…king#4865) Contracts on a trait method whose type carries a concrete generic argument — `<CStr as Index<RangeFrom<usize>>>::index`, or a concrete instantiation of a generic self type — failed to resolve with `MissingTraitImpl`. `resolve_ty` returned the definition's identity type (`RangeFrom<usize>` came back as `RangeFrom<Idx>`), so the arguments written in the path were parsed but never applied. This PR applies them, in `resolve_ty`'s `Type::Path` arm, via the new `instantiate_path_args`. ## Verification The tests live in the standard `tests/kani` and `tests/expected` suites (CI runs them); each has a definite outcome: | test (`-Zfunction-contracts`) | expect | |---|---| | `tests/kani/FunctionContracts/generic_argument_instantiation.rs` | **6 targets verify.** A primitive argument resolved before (regression guard); the five generic shapes resolve *only* with this change — a generic argument, a generic self type (associated-type return, `where`-bounded method), a concrete DST self, a generic `impl` at a concrete instantiation, and a type with a lifetime parameter beside a type parameter (lifetime erased). | | `tests/expected/function-contract/generic_arg_unimplemented.rs` | **still fails `MissingTraitImpl`** *(expected-output test — this diagnostic is the pass)* — `<S as Generic<Wrap<u16>>>::generic` is genuinely not implemented, so valid targets resolve without over-resolving invalid ones. | | `tests/expected/function-contract/generic_method_unimplemented.rs` | **still fails `MissingTraitImpl`** *(expected-output test — this diagnostic is the pass)* — a trait method with its own generic parameter (`fn compute<T>`) is out of scope (see below). | Confirmed on nightly-2026-09-22 (Kani 0.68.0, CBMC 6.11.0): all six verify; with the fix reverted, the generic targets fail to resolve while the primitive resolves. The `cross_module_multiple_impls` (7/7), `multiple_inherent_impls` (3/3), and resolver unit (41/41) suites are unchanged. Mechanism: this is the "trait functions with generic parameters" limitation described in [model-checking#1997](model-checking#1997 (comment)). The trait's arguments already reach trait-impl resolution, but each came back uninstantiated from `resolve_ty`, and full `Instance::resolve` cannot match a concrete impl from a free parameter. Making the arguments concrete fixes that; any shape that cannot be resolved (const-generic arguments, omitted defaulted parameters) keeps the old uninstantiated type, so nothing that resolved before changes. No partial-resolution machinery is needed — with concrete arguments, the existing full resolution matches. These are not hypothetical. An iterator adapter's `__iterator_get_unchecked` (a generic self type) is another std-library method of this shape: the Challenge 16 and Challenge 24 submissions ([model-checking/verify-rust-std#549](model-checking/verify-rust-std#549), [model-checking/verify-rust-std#689](model-checking/verify-rust-std#689)) disclose it and fall back to `kani::assume` mirror harnesses. What this change reaches there: closure-free adapters resolve (`Cloned`, `Zip`); `Map`/`Filter` do not, since `fn`/closure types aren't supported by `resolve_ty`. Challenge 24's `<vec::IntoIter<u8> as Iterator>::__iterator_get_unchecked` resolves only with the allocator written out (`IntoIter<u8, std::alloc::Global>`, which needs `allocator_api`): omitted trailing parameters with declared defaults are not filled and keep the uninstantiated type. Related to model-checking#1997 — this handles a type carrying concrete generic arguments: a generic trait argument, or a generic self type instantiated to concrete types, against a concrete or generic `impl`. A trait method with its own generic parameter still fails to resolve — that parameter is not part of the path, so nothing here binds it. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
wodex1nhaoIeng
pushed a commit
to wodex1nhaoIeng/kani
that referenced
this pull request
Oct 2, 2026
…el-checking#4915) A `proof_for_contract` target that omits a trailing generic parameter with a declared default failed to resolve with `MissingTraitImpl`. For example `<Vec<u8> as Trait>::m`, or the natural spelling of `<vec::IntoIter<u8> as Iterator>::__iterator_get_unchecked`, a target in the Challenge 24 submission (model-checking/verify-rust-std#689); both omit the allocator parameter. The parameter-count check kept the uninstantiated type instead of filling the default. This PR fills an omitted trailing parameter from its declared default, instantiated with the arguments so far, matching what rustc does for omitted arguments. The logic lives in `default_type_arg`, called from `instantiate_path_args`'s substitution loop. A path written with no generic arguments at all (a bare `Wrapper` for `struct Wrapper<T = u8>`) is treated as an empty argument list, so an all-defaulted type resolves too. Parenthesized arguments keep the uninstantiated type. ## Verification | test (`-Zfunction-contracts`) | expect | |---|---| | `tests/kani/FunctionContracts/generic_default_argument_fill.rs` | **5 targets verify.** A std container default (`Vec<u8>` fills `A = Global`); a sibling impl with a distinct postcondition (`Vec<u16>`), so each harness only verifies if resolution picks its own impl; a default referencing an earlier parameter (`Pair<T, U = T>` at `Pair<u8>` fills `U = u8`); the explicit spelling (`Vec<u16, std::alloc::Global>`) unchanged; and an all-defaulted type written with no argument list (`AllDefault` for `struct AllDefault<T = u8>`) fills `T = u8`. | | `tests/expected/function-contract/generic_default_missing_required.rs` | **still fails `MissingTraitImpl`** *(expected-output test; this diagnostic is the pass)*. `NoDefault<u8>` omits a parameter with no default, so only declared defaults are filled. | | `tests/expected/function-contract/generic_default_missing_bare.rs` | **still fails `MissingTraitImpl`** *(expected-output test; this diagnostic is the pass)*. `NoDefaultBare` written with no argument list omits a parameter with no default, so the no-arguments path still keeps the uninstantiated type. | | `tests/expected/function-contract/generic_default_const_param.rs` | **still fails `MissingTraitImpl`** *(expected-output test; this diagnostic is the pass)*. `WithConst<u8>` omits a defaulted **const** parameter; only defaulted type parameters are filled, so the const default is not filled and the target keeps the uninstantiated type. | The four original targets were confirmed locally when this PR was first submitted (`generic_default_argument_fill` 4/4, the two negatives failing `MissingTraitImpl`, resolver suites unchanged at `generic_argument_instantiation` 6, `cross_module_multiple_impls` 7, `multiple_inherent_impls` 3). This revision adds the no-arguments case (`check_all_parameters_defaulted`) and the `generic_default_missing_bare` negative; CI runs the full suite on the pinned toolchain. Scope: type parameters only. A const parameter (with or without a default), and any argument that cannot be resolved, keep the uninstantiated type, as before. Defaults are instantiated structurally (no normalization), matching how `resolve_ty` already hands `tcx.type_of` results forward. Resolves model-checking#4914. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
This branch has not been deployed
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.
Towards #285.
Kani harnesses for Challenge 24: Verify the safety of
Vecfunctions part 2 —Vec::IntoIterand the specialization helpers it routes through (spec_extend,spec_from_iter,spec_from_iter_nested,spec_from_elem,from_elem,extract_if). 47 harnesses across all 22 listed functions. Each runs the function's real shipped body — no#[cfg(kani)]rewrite, nokani::assume(false)on a real branch — at symbolic length where tractable, fixed small sizes where noted, and asserts the observable effect rather than just absence of UB. All pass viascripts/run-kani.sh.Unbounded length and generic
Tare not met; both are disclosed under Limitations. Whether representative-type, bounded coverage satisfies these clauses is a judgment we defer to the reviewers.What the harnesses check
kani::slice::any_slice_of_array+to_vec, so the pointer-walking loops (next/next_back,fold/try_fold,advance_by/advance_back_by) run a symbolic number of iterations up to the backing size (64 forIntoIter, smaller where noted).next/next_backreturn the exact first/last element and shrink length by one;advance_by(k)returnsOkiffk <= len;foldvisits exactlylenelements;from_elem(e, n)yields lengthnwith everyv[j] == e;size_hint == (len, Some(len)).try_foldshort-circuits (Err) in both au8and aDrop-carrying variant, exercisingIntoIter's realDrop, so a double-drop on the short-circuit path would be caught.extract_if::nextwrites through the realvec.as_mut_ptr().add(i).T::IS_ZSTbranch (byte-walkingend, fixedptr) is verified onVec<()>at symbolic length.__iterator_get_unchecked. Already ships#[requires(i < self.len())]+kani::modifies(self)onmainwith no exercising harness. Verified here directly as aproof_for_contractat two instantiations —u8(from arbitrary reachable states up to the backing bound) and the odd-stride[u8; 3](see Upstream Kani contributions).kani::coverreachability witnesses across the suite.Element-type coverage (+8 harnesses)
These reach properties of
Ttheu8/()instantiations don't. Most share one generic body instantiated per shape, mirroring its_u8counterpart; the[u8; 3]get_unchecked row is a secondproof_for_contract(see Upstream Kani contributions).check_into_iter_next_al16next#[repr(align(16))]): 16-byte stridecheck_into_iter_next_boolnextcheck_into_iter_next_droptokennextDroptypecheck_into_iter_next_back_droptokennext_backcheck_into_iter_get_unchecked_arr3__iterator_get_uncheckedadd(i)check_spec_extend_intoiter_droptokenspec_extend(IntoIter)forget_remaining_elements, no double-dropcheck_from_iter_intoiter_droptokenSpecFromIter(IntoIter)ManuallyDrop+from_partsownership transfercheck_extract_if_next_droptokenExtractIf::nextShapes:
Al16(#[repr(align(16))]),[u8; 3](odd stride),bool(validity niche),DropToken(realDrop). Verified at the pinned Kani 0.68.0 / CBMC 6.11.0.Arbitrary reachable iterator states (+13 harnesses)
The harnesses above start from a fresh iterator (
ptr == buf). These re-check the surface from arbitrary reachable states: a symbolic number of elements consumed from each end, soptrsits at any valid interior position. State is built throughadvance_by/advance_back_by(each verified by its own harness), reaching every state iteration can produce, and results are checked against a pre-conversion snapshot at the advanced offsets.Covered from these states (13 of the 14
IntoItertargets):next,next_back,size_hint,as_slice,as_mut_slice(with a write-through check),advance_by,advance_back_by,fold,try_fold,next_chunk,into_vecdeque,__iterator_get_unchecked(itsu8proof_for_contract), andDropof a both-ends-consumedDropTokenvector.forget_allocation_drop_remainingshares the remaining-range drop path.Upstream Kani contributions
__iterator_get_uncheckedlives in a generic trait impl (Iterator for IntoIter). The harness names the allocator explicitly (<IntoIter<u8, Global> as core::iter::Iterator>::…) so it resolves as aproof_for_contracttarget. The enabling upstream fix is model-checking/kani#4865 (instantiate path generic arguments during type resolution), which we authored and which merged into this pin. The harnesses verify the method's shipped#[requires(i < self.len())]+kani::modifies(self)contract directly, at theu8/Globaland[u8; 3]/Globalinstantiations.How to verify
Scoped to these harnesses, from the repo root:
Expected:
Complete - 47 successfully verified harnesses, 0 failures, 47 total., every cover satisfied.Limitations (disclosed)
IntoIter, smaller where noted). A loop-contract route to genuine unboundedness was measured, but CBMC does not terminate on thekani::mem::same_allocationinvariant the havocedIntoIterheap-pointer loops need; the same invariant over a stack array does verify, so the limit is specific to this heap-pointer shape, not the predicate.T: not met. Representative element types only (u8,(), aDroptoken, and the over-aligned / odd-stride / niche shapes above). Whether representative-type coverage satisfies the clause is a judgment we defer to the reviewers; we will complete a single-generic-body conversion promptly on a favorable ruling.extract_if::nextand the defaultfrom_iterexceed the CI-standard--object-bits 12budget at larger sizes; theDrop-token harnesses use a fixed size of 4 (drop obligations are per-element identical). Each bound is noted in-code.spec_extendpre-sizes the destination, so its copy path is verified; element-by-element growth routes throughextend_desugared, andSpecFromElem'sT: Clonedefault throughextend_with— both Challenge 23 targets, not claimed here.+976 / −0across 7 files); no runtime logic is modified.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.