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

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Sun Nov 26 01:47:40 PST 2023


zyn0217 wrote:

Thank you again for bothering with this, and sorry for not responding for over a week.

Just now, I replicated the experimentation

> 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.

 and had to admit that I was wrong previously. Something must be wrong with my recollection, but I could clearly remember that I'd run into the same pitfall my first time. (I was about to write the comment to highlight this, but soon, I gave up taking this approach since it looks more constricted than I thought. And here I'm explaining it.)

> 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.

The contrived example involving template template parameter is one obstruction; Another more common example could be such. (Which is simplified from the test case):

```cpp
template <typename T>
struct Outer {
  template <typename U>
  void foo(U arg) {
     arg.fo^o();
  }
};

struct Widget {
  void foo();
};

Outer<int>().foo(Widget());
```

Here, we want to find the only instantiation before locating `foo().` Which is `Outer<int>::foo<Widget>.` To locate that, we shall find the templated Decl for `foo`, which could be done by conducting the `ParmVarDecl -> TemplateParmVarDecl -> getDeclContext -> FunctionTemplateDecl` dance. Everything goes well until the specialization set for the `FunctionTemplateDecl` is **empty**. Hence, our `getOnlyInstantiation` returns null, which is crazy!

This is because when clang performs the instantiation for member templates, it creates a new `FunctionTemplateDecl` from the primary template (i.e. where we start from) and links the instantiation declaration to that new `Decl`.

https://github.com/llvm/llvm-project/blob/1449b349ac4072adb1f593234c1d6f67986d3b6a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp#L2184-L2203

And what makes it worse is, as of now, we don't have any approach to retrieve the new templated `Decl` from the primary `Decl`:

https://github.com/llvm/llvm-project/blob/1449b349ac4072adb1f593234c1d6f67986d3b6a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp#L4789-L4803

("// FIXME: New needs a pointer to Tmpl", I was quite frustrated when I read it!)

(This patch first walks up the `DeclContext` to collect the very outer `Decl` for templates; then, the visitor could access the later-created template decl and its instantiation from the enclosing template. Again, I didn't realize the visitor resolved this issue this way (by accident), so I'm sorry for not being so straightforward.)

> The implementation of "Approach 2" could look similar to the current InstantiatedDeclVisitor in the patch, except the node type we are searching for is not limited to a Decl, it can also be other node types like Stmt etc.

Exactly. And this is why the patch is marked as "Take 1" :)

> However, the entry point to the check can be the same (e.g. for resolving a dependent member expr, we'd want to do the "only instantiation" check in the same cases where we call HeuristicResolver::resolveMemberExpr() from external code), so the functionality could still live in HeuristicResolver for organizational purposes.

Yeah, I'd love to extend the HeuristicResolver to make it more accurate and not just dependent on the mere name lookup.

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


More information about the cfe-commits mailing list