Fix panic from cross-crate FnDef impl-path collision#2559
Conversation
| // The test defines 12 `Clone` impls in the crate root, which are enough to | ||
| // collide with the `Clone` impls for `Ghost` and `Tracked` in | ||
| // `verus_builtin` at the same disambiguator (brought in by `["vstd"]`). |
There was a problem hiding this comment.
This is quite a fragile test in that it relies on a disambiguator collision with definitions in verus_builtin.
The ideal test would setup two synthetic crates to force a collision. However, I'm not sure there's an easy way to setup multi-crate tests? Most rust_verify_test tests are single file.
tjhance
left a comment
There was a problem hiding this comment.
The definition of impl_fndef still seems a little dubious to me:
pub(crate) fn impl_fndef(fun: &Fun, kind: ClosureKind) -> Ident {
let joined =
fun.path.segments.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(PATH_SEPARATOR);
Arc::new(format!("{}{}{}", PREFIX_IMPL_FNDEF, kind, joined))
}
e.g. it seems like there could be a collision if the first segment of joined starts with "Once" or "Mut"?
Can we come up with a different strategy entirely here? There doesnt' seem to be any reason to collapse the segments. We could do something like:
fun.path.push_segment(format!("{}{}", PREFIX_IMPL_FNDEF, kind))
Per review feedback on verus-lang#2559: rather than flattening the function's path segments into a single `impl_fndef` ident and stamping the crate back on, push the per-kind marker as its own segment onto the function's path. This keeps the crate in `Path.krate` and avoids ambiguity at the kind/segment boundary (e.g. kind `Fn` followed by a segment starting with `Once` vs kind `FnOnce`).
The synthetic `FnDef : Fn{,Mut,Once}` trait-impl that Verus generates for
each `clone` method built its impl path from the function's path segments
only, dropping the crate and placing it under CrateId::Internal. Two
crate-root impls in different crates can share a rustc disambiguator
(e.g. `impl&%7` is both verus_builtin's `Clone for Ghost` and a user
crate's clone impl), so their synthetic paths collided, tripping the
`!trait_impl_map.contains_key(..)` assertion in GlobalCtx::new.
Carry the function's own crate in the impl path instead. Adds a
regression test.
Per review feedback on verus-lang#2559: rather than flattening the function's path segments into a single `impl_fndef` ident and stamping the crate back on, push the per-kind marker as its own segment onto the function's path. This keeps the crate in `Path.krate` and avoids ambiguity at the kind/segment boundary (e.g. kind `Fn` followed by a segment starting with `Once` vs kind `FnOnce`).
950dfb8 to
67f7e9a
Compare
Much nicer! Implemented. Please take another look @tjhance. |
The synthetic
FnDeftrait-impl that Verus generates for eachclonemethod built its impl path from the function's path segments only, dropping the crate and placing it underCrateId::Internal.Two crate-root impls in different crates can share a
rustcdisambiguator. For exampleverus_builtinhasClone for Ghostassigned toimpl&%7, which can collide with a definition in a user crate.This PR uses the function's krate in the path instead of
CrateId::Internal. Adds a regression test.Related: #2474
By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.