clice-project / clice

MIT License
24 stars 1 forks source link

Improve the handling of templates #2

Open 16bit-ykiko opened 1 week ago

16bit-ykiko commented 1 week ago

Issues in clangd:

It's often hard to provide completion suggestions for dependent types in C++, before a template is truly instantiated, we cannot make any accurate assumptions about it. But for code completion, accuracy is not so important; providing more possible results usually is more useful.

template<typename T>
void foo(std::vector<T> vec) {
    vec.^
}

Currently, clangd (in fact, clangd just uses the result generated by SemaComplete) can provide completion in the above code. The result is based on the main template of vector, rather than vector<bool>. It is not precise, but it is really useful. However, it can only handle dependent TemplateSpecializationType.

template<typename T>
void foo(std::vector<std::vector<T>> vec2) {
    vec2[0].^
}

In this case, clangd will give you no completion. Why? This is because the type of vec2[0] is std::vector<std::vector<T>>::reference, which is a DependentNameType. SemaComplete cannot handle it before actual instantiation. But, according to the C++ standard, of course we know the result of std::vector<std::vector<T>>::reference is std::vector<T>&, which can be further regarded as a TemplateSpecializationType. And then SemaComplete can provide completion for it.

The process of transforming a complex dependent type into a simple dependent type or even a non-dependent type is just pseudo template instantiation.

Although we can dig some holes for types in std, I want user-defined types to have the same status as types in the C++ standard library. So there must be a general way to implement such simplifications.

After lots of attempts, I made some progress. I wrote a resolver which can simplify std::vector<T>::reference and std::list<T>::reference in libstdc++ (the definition in libstdc++ is really complex and many nested templates) to T&.

The main process of the resolver is:

  1. For the most complex dependent type, type<...>::([template] name)*, we can simplify it recursively, so just considering type<Ts...>::name.

  2. According to the main template of type<Ts...>, lookup the name in it. If not found, find in its dependent base classes. If still not found, find in its partial specializations.

  3. If the result is a decl, and there is no template keyword before name, it must be a TypeAliasDecl or TypedefDecl; retrieve its type and use Sema::SubstType to substitute the template parameters in it. If there is a template keyword, it must be a ClassTemplateDecl or TypeAliasTemplateDecl; recursively simplify it.