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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits