Auto completion failed with nalgebra
rust-analyzer version: 0.3.2693 / 0.3.2702
rustc version: rustc 1.91.1 (ed61e7d7e 2025-11-07)
editor or extension: VSCode
relevant settings: None
repository link (if public, optional): -
code snippet to reproduce:
use nalgebra as na;
fn main() {
let a = na::dvector![1.0, 2.0];
// popup show here
a.
}
I have a very specific error with the nalgebra crate which fails to autocomplete on nalgebra stucts. Whenever I try to access a method or member variable of a nalgebra struct I don't have any autocompletion anymore and a popup shows in VSCode with the message: "Request textDocument/completion failed. Source: rust-analyzer"
if I click on "Go to output":
[Error - 11:27:17 AM] Request textDocument/completion failed.
Message: request handler panicked: cannot find !BoundConst { var: 4 } in param-env: ParamEnv {
clauses: [
...
This error only appeared with nalgebra, other crates just work fine. I have entirely reinstalled Rust and VSCode but this didn't help. The only way to fix this error for me was to switch back the rust-analyzer extension in VSCode to 0.3.2683. I hope this is enough information to reproduce the error.
Maybe a similar issue with https://github.com/rust-lang/rust-analyzer/issues/21173
I'm seeing the same exact error just after installing nalgebra. In case it helps, here is more of the error message:
Message: request handler panicked: cannot find `!BoundConst { var: 4 }` in param-env: ParamEnv {
clauses: [
Clause(
Binder {
value: TraitPredicate(!0: Sized, polarity:Positive),
bound_vars: [],
},
),
Clause(
Binder {
value: TraitPredicate(!1: Sized, polarity:Positive),
bound_vars: [],
},
),
...
Clause(
Binder {
value: OutlivesPredicate(
!1,
'static,
),
bound_vars: [],
},
),
Clause(
Binder {
value: TraitPredicate(!2: Dim, polarity:Positive),
bound_vars: [],
},
),
Clause(
Binder {
value: TraitPredicate(!2: Sync, polarity:Positive),
bound_vars: [],
},
),
...
]
}
stack backtrace:
0: std::panicking::panic_handler
at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library\std\src\panicking.rs:698
1: core::panicking::panic_fmt
at /rustc/ed61e7d7e242494fb7057f2657300d9e77bb4fcb/library\core\src\panicking.rs:75
2: <<I as ra_ap_rustc_type_ir::interner::Interner>::PlaceholderConst as ra_ap_rustc_type_ir::inherent::PlaceholderConst<I>>::find_const_ty_from_env
3: ra_ap_rustc_next_trait_solver::solve::<impl ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>>::compute_const_arg_has_type_goal
4: hir_ty::next_solver::infer::context::<impl ra_ap_rustc_type_ir::infer_ctxt::InferCtxtLike for hir_ty::next_solver::infer::InferCtxt>::enter_forall
5: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>::enter_canonical
6: ra_ap_rustc_type_ir::search_graph::SearchGraph<D,X>::evaluate_goal_in_task
7: ra_ap_rustc_type_ir::search_graph::SearchGraph<D,X>::evaluate_goal
8: ra_ap_rustc_type_ir::inherent::Predicate::as_normalizes_to
9: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>::try_evaluate_added_goals
10: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>::evaluate_added_goals_and_make_canonical_response
11: hir_ty::next_solver::infer::context::<impl ra_ap_rustc_type_ir::infer_ctxt::InferCtxtLike for hir_ty::next_solver::infer::InferCtxt>::probe::{{closure}}
12: hir_ty::next_solver::infer::context::<impl ra_ap_rustc_type_ir::infer_ctxt::InferCtxtLike for hir_ty::next_solver::infer::InferCtxt>::probe
13: hir_def::_::<impl hir_def::BlockId>::loc
14: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::probe::TraitProbeCtxt<D,I,F>::enter
15: ra_ap_rustc_next_trait_solver::solve::trait_goals::<impl ra_ap_rustc_next_trait_solver::solve::assembly::GoalKind<D,I> for ra_ap_rustc_type_ir::predicate::TraitPredicate<I>>::consider_impl_candidate
16: ra_ap_rustc_next_trait_solver::solve::assembly::<impl ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>>::assemble_impl_candidates::{{closure}}
17: hir_ty::method_resolution::TraitImpls::for_each_crate_and_block_trait_and_type
18: <hir_ty::next_solver::interner::DbInterner as ra_ap_rustc_type_ir::interner::Interner>::for_each_relevant_impl
19: ra_ap_rustc_next_trait_solver::solve::trait_goals::<impl ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>>::compute_trait_goal
20: hir_ty::next_solver::infer::context::<impl ra_ap_rustc_type_ir::infer_ctxt::InferCtxtLike for hir_ty::next_solver::infer::InferCtxt>::probe
21: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::probe::ProbeCtxt<D,I,F,T>::enter
22: ra_ap_rustc_next_trait_solver::solve::normalizes_to::<impl ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>>::compute_normalizes_to_goal
23: hir_ty::next_solver::infer::context::<impl ra_ap_rustc_type_ir::infer_ctxt::InferCtxtLike for hir_ty::next_solver::infer::InferCtxt>::enter_forall
24: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>::enter_canonical
25: ra_ap_rustc_type_ir::search_graph::SearchGraph<D,X>::evaluate_goal_in_task
26: ra_ap_rustc_type_ir::search_graph::SearchGraph<D,X>::evaluate_goal
27: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>::try_evaluate_added_goals
28: ra_ap_rustc_next_trait_solver::solve::alias_relate::<impl ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>>::compute_alias_relate_goal
29: hir_ty::next_solver::infer::context::<impl ra_ap_rustc_type_ir::infer_ctxt::InferCtxtLike for hir_ty::next_solver::infer::InferCtxt>::enter_forall
30: ra_ap_rustc_next_trait_solver::solve::eval_ctxt::EvalCtxt<D,I>::enter_canonical
31: ra_ap_rustc_type_ir::search_graph::SearchGraph<D,X>::evaluate_goal_in_task
32: ra_ap_rustc_type_ir::search_graph::SearchGraph<D,X>::evaluate_goal
33: hir_ty::next_solver::fulfill::FulfillmentCtxt::try_evaluate_obligations
34: core::ops::function::FnOnce::call_once
35: hir_ty::infer::unify::could_unify_impl
36: hir::Type::could_unify_with
37: ide_completion::render::match_types
38: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::try_fold
39: ide_completion::render::function::render_method
40: ide_completion::render::function::render_method
41: ide_completion::completions::Completions::add_method
42: <ide_completion::completions::dot::complete_methods::Callback<F> as hir::MethodCandidateCallback>::on_inherent_method
43: hir::Type::iterate_method_candidates_split_inherent
44: ide_completion::completions::dot::complete_dot
45: ide_completion::completions::complete_name_ref
46: ide_completion::completions
47: salsa::cancelled::Cancelled::catch
48: std::thread::local::LocalKey<T>::with
49: ide::Analysis::completions
50: rust_analyzer::handlers::request::handle_completion
51: <[T] as core::fmt::Debug>::fmt
I'm not sure that this is the same as #21173 since the ParamEnv was empty in that case, but not empty here. But I suppose we can see if #21235 fixes it?
Not fixed by https://github.com/rust-lang/rust-analyzer/pull/21235.
So here the situation is way more complicated than in https://github.com/rust-lang/rust-analyzer/pull/21235.
In short, what happens is that we try to unify two types originating from different GenericDefIds. Non-luckily for us, they both contain generic params. Trying to treat generic params from different owners uniformly is a catastrophic idea for the solver, and the source for the panic here.
Unfortunately, I believe our code is riddled with such things. This is why I was leaving such comments:
https://github.com/rust-lang/rust-analyzer/blob/fface27171988b3d605ef45cf986c25533116f7e/crates/hir/src/lib.rs#L2282
The solution, as I imagine it, is to have a separate hir type BoundType that can contain params. Type will be guaranteed to not contain TyKind::Param (recursively). Going froma BoundType to a Type is possible by instantiating it, or by verifying it contains no params. Actions between BoundParams of different owners are forbidden (fallible/panic).
@rust-lang/rust-analyzer What are your thoughts? Do you agree this is a better API?
I've always thought we need some kind of better API for this, yeah. BoundType seems a bit confusing to me, from the names I'd have expected them to work the other way around. BindableType would maybe express the same thing but clearer, or PolyType if we want to go a bit more jargon-y.
You don't necessarily go from a BoundType to a Type though, do you? If you instantiate e.g. an ADT's type in the context of a function, where that function might have its own type parameters, you just go from the ADT's set of params to the function's set of params. So I think you need to be able to instantiate a BoundType (or however it's called) with a set of other BoundTypes, in which case it again becomes a BoundType but in the other context. And then the question is how often we really need Type, isn't it? 🤔 Maybe that would be served better by a method that allows transferring a BoundType to another context, checking that it contains no type parameters. And in that case we don't need Type anymore, so we could call BoundType just Type 😄
I think that was part of the original idea of having Type carry a param env (trait env) in the first place, actually.