[clang-tools-extra] [clangd] Resolve the dependent type from its single instantiation. Take 1 (PR #71279)

Nathan Ridge via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 20 00:16:06 PST 2023


HighCommander4 wrote:

> I suspect it won't work with the present `TemplateTypeParm{Type,Decl}` models. `TemplateTypeParmDecl` per se is not tied to any `CXXRecordDecl` nor `FunctionDecl` that the template parameter is declaring with. (For the most seemingly-relevant part, i.e. the `DeclContext` it records, is the scope that the template defines.)

I've done some local experimentation, and what I'm seeing is that `TemplateTypeParmDecl::getDeclContext()` does return the `FunctionDecl` or `CXXRecordDecl` which owns the template parameter.

One tricky thing I found is that when starting from a template parameter **type**, it's important to use `dyn_cast<TemplateTypeParmType>(T)` rather than `T->getAs<TemplateTypeParmType>()`; the latter does more than just cast, it returns the canonical type whose associated Decl is null.

I also discovered some complications related to nested templates, and I have some thoughts on how to resolve them, but I'm going to suggest that we start with getting a simple case (e.g. just one level of templates, no nested templates) to work, and then tackle nested templates as a next step.

> > If so, find the argument type substituted for this template parameter, and replace the template parameter with this type.
> 
> I was thinking this way before getting into the details :-). Soon I realized that we had to tackle synthesized types e.g.
> 
> ```c++
> template <template <class, int> typename C, typename D, int E>
> void work() {
>   C<D, E> complicated_type;
>   // ^ Shall we perform the type substitution once again? ;)
> }
> ```

I'm not too concerned about this. The idea behind `HeuristicResolver` is to perform name lookups in the primary template as a heuristic.

So, to give an example:

```c++
template <typename T>
void work() {
  std::vector<T> a;
  a.pop_back();
}
```

here, `pop_back` is looked up in the primary template scope of `std::vector`; information from the argument list `<T>` is not used.

Now, applying this to your example:

```c++
template <template <typename> class A, typename T>
void work() {
  A<T> a;
  a.pop_back();
}

// work() has one instantiation, with [A = std::vector, T = bar] 
```

applying the "only instantiation" heuristic will tell us that `A = std::vector`, and then we can look up `pop_back` in the primary template scope of `std::vector`; we still don't use information from the argument list `<T>`.

We could potentially envision future enhancements to make use of information from the "only instantiation" to make lookup inside template specialization types more accurate than just "use the primary template" (which could then apply to the `std::vector<T>` case as well, and e.g. return a more accurate result if the "only instantiation" is `T = bool`), but that seems like more of an edge case and I'd rather leave it to a future change.

https://github.com/llvm/llvm-project/pull/71279


More information about the cfe-commits mailing list