HighCommander4 wrote:

I thought some more about this.

I appreciate better now the question you asked here:

> 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? ;)
> > }
> > ```

My previous suggestion side-steps this by saying that we're only going to do 
lookups in the primary template scope of the argument for `C`, so we don't need 
to worry about substituting the arguments for `D` and `E`.

However, if in the future we want to be more accurate and e.g. handle cases 
where a template specialization resolves to something different than the 
primary template (e.g. a partial specialization), then your question becomes 
relevant: do we perform type substitution again with the arguments for `D` and 
`E`, or do we just fish out the already-substituted type from the "only 
instantiation"?

I realized that this consideration actually applies to any other "resolution" 
step performed in `HeuristicResolver`, including steps it already does today 
such as name lookup.

To illustrate, let's go back to the `vector` example:

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

To resolve the dependent member expression `a.pop_back`, `HeuristicResolver` 
currently performs name lookup in the primary template scope of `std::vector` 
for the name `pop_back`.

The name lookup step could also be avoided in principle if we had an "only 
instantiation": if we find the non-dependent `MemberExpr` that `a.pop_back` 
instantiates to in the "only instantiation", that will point directly to a 
`CXXMethoDecl`, and we have our result without having done the work of a name 
lookup in the primary template scope.

And this method can lead to more accurate results, e.g. if instead of 
`pop_back` we have an overloaded method name, the name lookup in the primary 
template scope will just return all overloads (since we're not using 
information about the argument types), whereas the instantiated expression in 
the "only instantiation" will point to the correct overload resolution result.

---

So, I think we could consider two alternative approaches to this:

**Approach 1**: What I suggested in the previous review: a limited use of the 
"only instantiation" in `HeuristicResolver` to map template parameter types to 
the type of their argument in the "only instantiation".

**Approach 2**: If there is an "only instantiation", then resolve any 
expression, type, etc. in the template body to the corresponding expression, 
type, etc. in the "only instantiation".

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.

"Approach 2" can be largely independent of `HeuristicResolver`: since we are 
finding a concrete result for the thing we're trying to resolve in the "only 
instantiation", no further resolution steps (e.g. name lookup, or anything else 
that `HeuristicResolver` does) should be necessary. 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.

https://github.com/llvm/llvm-project/pull/71279
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to