How to perform template substitution in the clang library?
abstract-syntax-tree, c++, clang
Solution
I've got a partial solution, the only caveat is that, I can't get `std::is_same<N<4>, N<4>>::value` to return `true`. Well I can live with that, as I could just define a `constexpr` method which operates on the values directly. But I hope someone could provide a correct answer for this.
I have put the complete solution and the modified input to https://gist.github.com/4178490.
I've found that to substitute arguments into a class template and instantiate it, one would:
- Use the arguments to turn the ClassTemplateDecl into ClassTemplateSpecializationDecl, and
- Instantiate the specialization using Sema::InstantiateClass method.
The method Sema::RequireCompleteType does indirectly call InstantiateClass, and requires less input, so I call this method instead. Therefore, we would write:
/**
* Instantiate a class template.
*/
ClassTemplateSpecializationDecl* instantiate(ASTContext& ast, Sema& sema,
DeclContext* parent,
ClassTemplateDecl* decl,
ArrayRef<TemplateArgument> args) {
void* ins_point;
auto retval = decl->findSpecialization(args.data(), args.size(), ins_point);
if (retval == nullptr) {
retval = ClassTemplateSpecializationDecl::Create(ast, TTK_Class, parent,
{}, {}, decl,
args.data(), args.size(),
nullptr);
decl->AddSpecialization(retval, ins_point);
}
bool is_incomplete = sema.RequireCompleteType({}, ast.getTypeDeclType(retval),
diag::err_incomplete_type);
return is_incomplete ? nullptr : retval;
}
This method only works for ClassTemplateDecl. In the question we also have a TypeAliasTemplateDecl. For this, I am going to invoke the TemplateDeclInstantiator directly, since this is the only object which knows a TypeAliasTemplateDecl. Perhaps this method also works with ClassTemplateDecl, but I can't be sure as it seems not enough work is done using TemplateDeclInstantiator alone.
/**
* Instantiate a template alias (`template <...> using Foo = ...`).
*/
TypeAliasDecl* instantiate(ASTContext& ast, Sema& sema, DeclContext* parent,
TypeAliasTemplateDecl* decl,
ArrayRef<TemplateArgument> args) {
auto args_count = static_cast<unsigned>(args.size());
TemplateArgumentList arg_list {TemplateArgumentList::OnStack,
args.data(), args_count};
MultiLevelTemplateArgumentList multi_arg_list {arg_list};
TemplateDeclInstantiator instantiator {sema, parent, multi_arg_list};
auto instantiated = instantiator.Visit(decl);
if (auto inst_decl = dyn_cast<TypeAliasTemplateDecl>(instantiated)) {
return inst_decl->getTemplatedDecl();
}
return nullptr;
}
(I skipped FunctionTemplateDecl, it is out of the scope of my question.)
Problem
Specifically, we have a C++ source file like this: ``` template <int n> struct N {}; struct B { template <typename M> using A = typename std::conditional<std::is_same<M, N<4>>::value, int*, void*>::type; }; template <typename T, T value> struct F : B {}; template <> struct F<decltype(&fopen), &fopen> : B { template <typename M> using A = double*; }; template <> struct F<decltype(&fclose), &fclose> : B { template <typename M> using A = typename std::conditional<std::is_same<M, N<16>>::value, void*, char**>::type; }; // More specialization of 'F' follows. ``` It is easy to find the ClassTemplateDecls of `N` and `F`, and the QualType and FunctionDecl of the function pointers `&fopen`, `&fclose`, etc. But the problem is how to substitute these arguments into N, F and F::A without modifying the source code. The question is: - How do I evaluate `F<decltype(&fprintf), &fprintf>::A<N<4>>` and know that it is an `int*`? - How do I evaluate `F<decltype(&fopen), &fopen>::A<N<7>>` and know that it is a `double*`? - and so on...