<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/64820>64820</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
RecursiveASTVisitor (RAV) does not see the parameters of the __invoke method of lambdas
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
smcpeak
</td>
</tr>
</table>
<pre>
In the clang AST, a lambda expression implicitly defines a `CXXRecordDecl` that has an `__invoke` method. The `__invoke` method has the same parameter names and types as the lambda, and those parameters are visible in the AST dump. However, `RecursiveASTVisitor` (RAV) does not see them.
For example, with this input:
```
void f(int a, int b)
{
[](int x) -> void {};
}
```
and this `RecursiveASTVisitor`:
```
class RAVTest : public clang::RecursiveASTVisitor<RAVTest> {
public:
// Just to be sure we're visiting everything we can.
bool shouldVisitTemplateInstantiations() const { return true; }
bool shouldVisitImplicitCode() const { return true; }
bool VisitDecl(clang::Decl *decl)
{
// Print all the Decl nodes.
llvm::outs() << decl << ": " << decl->getDeclKindName() << "\n";
// Print all parameters of FunctionDecl nodes.
if (auto functionDecl = dyn_cast<clang::FunctionDecl>(decl)) {
for (unsigned i=0; i < functionDecl->getNumParams(); ++i) {
llvm::outs() << " param " << i << ": " <<
functionDecl->getParamDecl(i) << "\n";
}
}
// Print the child declarations of DeclContext nodes.
if (auto declContext = dyn_cast<clang::DeclContext>(decl)) {
for (clang::Decl *child : declContext->decls()) {
llvm::outs() << " child decl: " << child << "\n";
}
}
return true;
}
};
// This is invoked by the usual boilerplate.
void runRAVTest(clang::ASTContext &astContext)
{
clang::TranslationUnitDecl *tu = astContext.getTranslationUnitDecl();
tu->dump(llvm::outs());
RAVTest ravTest;
ravTest.TraverseDecl(tu);
}
```
when the program is run it produces output like this (which I've then annotated a little):
```
TranslationUnitDecl 0x5599eca39d38 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0x5599eca3a5a0 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0x5599eca3a300 '__int128'
|-TypedefDecl 0x5599eca3a610 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0x5599eca3a320 'unsigned __int128'
|-TypedefDecl 0x5599eca3a988 <<invalid sloc>> <invalid sloc> implicit __NSConstantString '__NSConstantString_tag'
| `-RecordType 0x5599eca3a700 '__NSConstantString_tag'
| `-CXXRecord 0x5599eca3a668 '__NSConstantString_tag'
|-TypedefDecl 0x5599eca3aa20 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0x5599eca3a9e0 'char *'
| `-BuiltinType 0x5599eca39de0 'char'
|-TypedefDecl 0x5599eca80b78 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag[1]'
| `-ConstantArrayType 0x5599eca80b20 '__va_list_tag[1]' 1
| `-RecordType 0x5599eca3ab10 '__va_list_tag'
| `-CXXRecord 0x5599eca3aa78 '__va_list_tag'
`-FunctionDecl 0x5599eca80d38 <tmp.cc:1:1, line:4:1> line:1:6 f 'void (int, int)'
|-ParmVarDecl 0x5599eca80be8 <col:8, col:12> col:12 a 'int'
|-ParmVarDecl 0x5599eca80c68 <col:15, col:19> col:19 b 'int'
`-CompoundStmt 0x5599eca81698 <line:2:1, line:4:1>
`-LambdaExpr 0x5599eca81530 <line:3:3, col:22> '(lambda at tmp.cc:3:3)'
|-CXXRecordDecl 0x5599eca80f88 <col:3> col:3 implicit class definition
| |-DefinitionData lambda pass_in_registers empty standard_layout trivially_copyable literal can_const_default_init
| | |-DefaultConstructor defaulted_is_constexpr
| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveConstructor exists simple trivial needs_implicit
| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment
| | `-Destructor simple irrelevant trivial
| |-CXXMethodDecl 0x5599eca810d0 <col:16, col:22> col:3 constexpr operator() 'auto (int) const -> void' inline
| | |-ParmVarDecl 0x5599eca80e50 <col:6, col:10> col:10 x 'int'
| | `-CompoundStmt 0x5599eca81190 <col:21, col:22>
| |-CXXConversionDecl 0x5599eca813b0 <col:3, col:22> col:3 implicit constexpr operator void (*)(int) 'auto (*() const noexcept)(int) -> void' inline
| |-CXXMethodDecl 0x5599eca81468 <col:3, col:22> col:3 implicit __invoke 'auto (int) -> void' static inline
| | `-ParmVarDecl 0x5599eca81348 <col:6, col:10> col:10 x 'int'
| `-CXXDestructorDecl 0x5599eca81558 <col:3> col:3 implicit referenced ~(lambda at tmp.cc:3:3) 'void () noexcept' inline default trivial
`-CompoundStmt 0x5599eca81190 <col:21, col:22>
0x5599eca39d38: TranslationUnit
child decl: 0x5599eca3a5a0
child decl: 0x5599eca3a610
child decl: 0x5599eca3a988
child decl: 0x5599eca3aa20
child decl: 0x5599eca80b78
child decl: 0x5599eca80d38
0x5599eca3a5a0: Typedef
0x5599eca3a610: Typedef
0x5599eca3a988: Typedef
0x5599eca3aa20: Typedef
0x5599eca80b78: Typedef
0x5599eca80d38: Function
param 0: 0x5599eca80be8 } Normally, function parameters are
param 1: 0x5599eca80c68 } entered both into the DeclContext
child decl: 0x5599eca80be8 } list and the FunctionDecl::ParamInfo
child decl: 0x5599eca80c68 } array.
child decl: 0x5599eca80f88
0x5599eca80be8: ParmVar
0x5599eca80c68: ParmVar
0x5599eca80f88: CXXRecord
child decl: 0x5599eca810d0
child decl: 0x5599eca813b0
child decl: 0x5599eca81468
child decl: 0x5599eca81558
0x5599eca810d0: CXXMethod } The operator() works normally, with
param 0: 0x5599eca80e50 } its parameter in both places.
child decl: 0x5599eca80e50 }
0x5599eca80e50: ParmVar
0x5599eca813b0: CXXConversion
0x5599eca80e50: ParmVar
0x5599eca80e50: ParmVar
0x5599eca81468: CXXMethod } But the __invoke method DeclContext
param 0: 0x5599eca81348 } is missing its parameter, which is
0x5599eca80e50: ParmVar consequently never visited by RAV.
0x5599eca81558: CXXDestructor
```
The key observation, which I think indicates a bug, is that `0x5599eca81348`, the `ParmVarDecl` of the `__invoke` method, is seen by the text dumper but not by RAV.
I think this is a bug because RAV [claims](https://clang.llvm.org/doxygen/classclang_1_1RecursiveASTVisitor.html#details) to "visit every part of the explicit source code exactly once". Of course, in this case one could argue that the "explicit" qualification excuses this behavior, but it seems nonetheless unintentional.
As explained in the annotations, the reason for the difference between RAV and the text dump is that the `__invoke` parameters are missing from their `DeclContext` list. `RecursiveASTVisitor` relies on the `DeclContext` list to walk parameters, whereas `ASTNodeTraverser` (used by the text dumper) has different logic, specifically choosing to traverse the parameters via the `FunctionDecl` instead. (See `ASTNodeTraverser::Visit(const Decl*)` and `ASTNodeTraverser::VisitFunctionDecl`.)
I'll also note that the output above shows another anomaly, namely that the `ParmVarDecl` of the `operator()` method, `0x5599eca80e50`, is seen by RAV four different times. I haven't dug into why that happens.
This behavior has been observed with Clang+LLVM-16.0.0 as well as a somewhat recent trunk build from source.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJysWk1z4zbS_jX0pcsqCrQo6uCDLI_r9bvJ7NTYO5WbCiRbItYkwACgZF32t281QEqkPmeSTWVkigAa3U9_oiFujFhLxMdg8hRMnu94YwulH02V1cg_7lKV7x5fJdgCISu5XMP87T1gC-BQ8irNOeBnrdEYoSSIqi5FJmy5gxxXQqIBDkEcLv744ztmSufPmJVBHIItuIWCG-CSxpdLITfqA2moQluofATwXuD5MbeQ-DG8Qqi55hVa1CB5RRvKHOyupic_y7PpWKahQpneIgNcI2yEEWmJILyc87d3yJuqHgH8n9riBjWtDuLwO2aNNmKD87f3H8IIqzSxFbDk-_xHwGaQKzQglQWDSKSqURA-B-Hcf74oDfjJq7pEIrgVtgBbCANC1o0Nonl_chCH7f_u60aJHFYBS4S04KShhzRgs3b69Mk_AHhFtlM_ia37IPoCjgJNmz4H0VO36vnsbv7T4yXMZdGv85yV3Bj4Pv_xjsZCEM2hbtJSZN6QaG00P0c3WrRriO29YH7tfkeAgL0E7AX-vzEWrIIUwTQaYYsBm7ZKtUKugRS4swU9bhEyLkcdhVSpEkyhmjJ3e79jVZfc4qs0lksruBVKmoAlBGKmpLEAQByBRttoCVY3GERPsMfxlOZr6xMLleMxpcN_12gOKDuazo1Y0oOR3kDA5rkbme0hOljFHq9v2plQWTpbdwulytF0qJTlpvJEVWM74YNoEUQLyN02_jlgjFQaMNYfJVNbo-PwH0LmX3mFQxK0bLKQbvXTUMAzLPYcVa3gpZEZqeSUaQAQK3JE3lgFq_68IHqGfCeXGSeDWvRA65MLoi8BSzr4iN0-dAArpYl8I120zEEE0XNIahIk2GDHFoKvTfWNuG8hdCplTwF7EkPy1_AmcD0GfZzFRRW0JM9w41hp7UZcV0dnkc89vRzb4pGqXHYoRJk7G-DaOw6pjLZcKGnx017TWN6bdllhPWI_qa8zLuL5JNx6mxJK9LVT1ilBuK2oAwJHjtFt-fcQH8aH9v1hXj-s9z-9nt5dnqFUQ8k0h3TnlNaYhpeQKlGidqFv1Es3upFdIB4gOX973-uKxdzYTienyai36l1zaUpnGf-SPoaROmzjFH6gMlqjPTN370UDWGzjNNdUdcCScwo6s2afkTTf-CzTcdu-GL1rvkFtsN3XNgMqV3PmtkBfRNRarclvhSEcQVh6kzcZGlCNrRsLpfjANr-yZFuIrIDXgE03rnKQwKVUllvMqdQS1lLRMLuecM9BHH5OJrMZZjya5VHSGqGQG16KHEypMvIkyrOnb1txF_fvuxpzXB3R4xMe_gq9fXkIVNPZMUuWZEDT7lvApvstqeS4f2pEaYWk3fvbRmF4adklTuPxX-a06bO6D_-_zDMLb66_xPws-SW19Zn_-rZQvpZ5s5oKIIfb8dul5esTQXzBfizHtMP-Jg1wVPaV_0AbcfJzRC4hwtlfVmfq9bOszHLDl6UwTq9ZwSldzE9Q-KaEtKiPYZhheHEZXLGDWX5YeFvQJEynf1n1naA9KZfdF4fz5GnsjglHEncamWvNd0P2kzBl4RVCMIZjGM5bUTo-Q2aI4RXz4dPkyuo4vB-UiT3u2_hnq3qU0UFi7P6xBZRCYhDNH_y7L913Go-BipSpPzy5I1V79nK5ZXootBf337iufnB9okN0u2aKqoKEVvvHMaOtumc6KLOpo_szRLO4R3Q86VGd9ajOID1D1Sm5qlUj8zdb2R7ZcTxzZFv52QV8utIjDu9_c4frL5-17pOZRGGPTOT-7TlkTm5iiCVtC4Fb2Culnd0Dl2AYdBD6QKySHhDRQfbo4Av-GOoaEoLM4kDWkX7eDzxzu-9q1NyYpZBLjWth3PkDq9rugHwj5zpflnynGgtWi43gZblbZqre8bREStaoeUknzaU76i1zXPGmtEvaZ1jxLfps0BznfrrJrNLQLsN8KYynhJ-1vkRgoepdf7UhALBjEAre0lj684REzM1yD1L3sDyaNwTLbfS72mB_I_wUxprj_Yb0rzE9dx2oCqX9HzF7sgtxfNjl7Kw4vH_GY-yE1ljihh84O7OWbPN315g6ss1xmIc9J42PXaAz1L1mQdWouVW6O1uwqTscdXGn6xzs2zkUc4V0bnZB8gvhAyc9znqMjcNe9Ajh8zR6HIN2KZKMZ70d2PhI9gswLpSkuvs0dI-jNOy7-QUkDy5_Ail0Edyl69kB0h7IbqjXoZEKPzOs7WD-z4F_2Sge4uRXJOnan2eMYcCJsdyK7Jo1UDVz1hrG0UPyd63B5-qDBx1vMZncDNMaV6hRZpjDf66nhn5Cpq8HPXUa6ULnkdv-XYMdnqXokH904upwGXYChkemm3Pi8e05syS5OYezW3RcgXlzDol6JL6Tg8T3ZevJMIlwZZi4vzJMjF8c9jxfGW410xWBnXg-kYTH8mNyaME-w1elK0rmpP2uiXZ0WTCkNz6iR3VZjx7S4QFzSJUtqGhU-55r1zK5qaCWQaLm6njflEcYNi_nQTR3Lb5XuVI3iXZcElFOdf7o5pJVcmwEjjea1UaVk-Esvjq88kawr-xusUDptOsqXZoSpTdNngLwzTmTyYm0bnvPrw_sfT2_F3icvLdKfxiQPYPaCltct0ZKyz2qwpre9ZaQ3ozqkme9NupFjXXE9v2q4dgV1TgYvaiHjPyLNG5t8RAnF9F8anxHeZ_72nu_M35zFkeX0Po4GqiEMUKuh5A6pbi-mzA3-HcVAf7ZoLTlDiRuUPsLJt9K_T7_MToRkYzIi3jIi1c6h2RDH7gDlRrUG5dSDgy-gi2E_AAhc5Fx6y5W02btzqLGX6cGcTiEgOizhUMyiMNe7g_iENSqGzi9YG2pGkTZ9Yldszdvqho1pI11d5wDuf1nx6Ztu82OSUgx441Bmg3B5CkruaiMv6IsrK0NhS_XpXbd4lFZbqqR0uuAveTqc7dG6YeMcePL8XJ85tZwVNiqDFiUo-WiNOSCrlpiTk3-EpBUbzvR8bMtPIxqdIaQqZxe8ow0rGSGAWMjgH-uIFONNujP_V60jBsEJWlRU-bA9bpBrwUHKmMd8YAx-LPhpViR2iib4GfWGDSeTooF3wjlLJFgFe7muKK4IdEWWKIx0EghLUpazcsB3HPjpODC3Uj5vnPbNvYXl177GrlR0l2J0NdcrNpKC1K0W9IyaaZLLHtV7y3rjKEcXaB37rXSqqLpQtOCvr_GoUtfoyvX6BpLgQaU7DY8s550uuXlR48B7yRIQtKi-dv7V5Vj18LvLugbc7j06BkzmUnBzR4TC6Vai4xomhozp7ay3EFWKOUkpBTekvZt_gMQG8E7zgfJOQ5B0HGE5yMgVt4QzzLqkrjDI2CJP4H4Cwh3YIlDp6HrC4_2HR1uZLx7BmxalsBLo8iDeybbXknwVG0QTKG2BrhUtkBNfyvuM5jkFZa7gVFcDCvDdDgMLoNQ5SKtD1W9oEMWuVKN7mnGigrNCF6h4BuKCVNS4tqXVdti1_2opK5RmtEwsvZczak7pV18oMXc_xBj4W6q2NNvv_34_X4cj8JRCNzAFgkwimRGVbilLTRmvlfRyA9IG0q_zu59HGl3vssfo3wWzfgdPo7jGYtm03gc3RWPaZ5E0XgWZatkHIcPk3E2y6Joxvg0jac443fikYUsCpNxwhiLJ9FozPPkgWUrNkk4jx5Y8BBixUW5D5R3wpgGH-OHhIV3JU-xNO7XPIxJ3IIbdJeOz3f6kdbcp83aBA8hOZQ5ULHClvh4xjmv_MDl6IL-XNZWq7anZu4aXT4OI_5a2KJJR5mqAvbi7u_8n_taq39jZgP24vg3AXtx8v03AAD__-96ENY">