对于以下代码
struct X {
int a;
};
int main() {
X x;
return 0;
}
clang AST 不显示
DestructorDecl
:
CXXRecordDecl 0x55a415f54f00 </home/gkxx/exercises/smfgen/tmp/../tmp/a.cpp:1:1, line:4:1> line:1:8 referenced struct X definition
|-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
| |-DefaultConstructor exists trivial
| |-CopyConstructor simple trivial has_const_param implicit_has_const_param
| |-MoveConstructor exists simple trivial
| |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| |-MoveAssignment exists simple trivial needs_implicit
| `-Destructor simple irrelevant trivial needs_implicit
|-CXXRecordDecl 0x55a415f55018 <col:1, col:8> col:8 implicit struct X
|-FieldDecl 0x55a415f550c0 <line:2:3, col:7> col:7 a 'int'
|-CXXConstructorDecl 0x55a415f55320 <line:1:8> col:8 implicit used X 'void () noexcept' inline default trivial
| `-CompoundStmt 0x55a415f557d8 <col:8>
|-CXXConstructorDecl 0x55a415f55458 <col:8> col:8 implicit constexpr X 'void (const X &)' inline default trivial noexcept-unevaluated 0x55a415f55458
| `-ParmVarDecl 0x55a415f55568 <col:8> col:8 'const X &'
`-CXXConstructorDecl 0x55a415f55618 <col:8> col:8 implicit constexpr X 'void (X &&)' inline default trivial noexcept-unevaluated 0x55a415f55618
`-ParmVarDecl 0x55a415f55728 <col:8> col:8 'X &&'
我尝试获取
CXXRecordDecl
的 struct X
,发现 decl->getDestructor()
返回 nullptr
。
这让我有点困惑,因为标准规定,没有用户声明的析构函数的类将有一个隐式声明的析构函数,并且在使用 odr 时它将是隐式定义的。那么什么是 odr-使用析构函数?这个
X
的析构函数是odr使用的吗?或者这只是我对 clang AST 和 clang::CXXRecordDecl::getDestructor
行为的误解?
请注意,相比之下,clang-AST 显示定义了
X
的默认构造函数,即使它不执行任何操作。
是的。引用basic.def.odr:
如果类的析构函数可能被调用,则它是 odr-used。
其中“潜在调用”有一个不平凡的定义,但简而言之, 此处为 true,因为
main()
声明了一个 X
类型的对象。
getDestructor()
是 nullptr
是什么意思?CXXRecordDecl::getDestructor()
只是说:
返回此类的析构函数 decl。
甚至不承认它可以返回
nullptr
,更不用说
解释这意味着什么。基于阅读源代码,我
结论这意味着析构函数是微不足道的,并且没有一个
无论如何都会导致创建隐式声明的条件
(见下文)已经很满意了。
它确实not意味着Clang声称析构函数不是 使用 ODR。这
Decl::isUsed()
方法声称报告此信息(请参阅有关
setIsUsed()
),但似乎并不完全准确;甚至
如果我通过添加一个来创建析构函数声明
virtual
函数,析构函数还没有标记isUsed()
。
是的——但为了保持一致,Clang 只需要生成编译后的输出 其行为“就好像”析构函数已被定义。缺少一个 特定的 AST 节点不会使其不合格。即使缺乏一个 编译目标文件中的定义则不然,只要它遵循 相关的ABI,从而配合其他工具,再次实现 所需的“好像”行为(假设源代码 符合单一定义规则)。
这似乎没有记录,所以我试图从 来源。隐式析构函数声明是通过以下方式创建的
Sema::DeclareImplicitDestructor()
在
SemaDeclCXX.cpp:13803
。
这个函数在好几个地方都被调用了,我没有全部遵循
链条向后,但对于第一个近似值,这不会
调用如果:
requires
)。请注意,这是最后一个条件,由于
x
的声明,导致 Clang 声明三个隐式
con讲师。
Sema::ForceDeclarationOfImplicitMembers
在 CXXRecordDecl
上强制 Clang 甚至声明隐式成员
否则它就不会这样做。例如,就在之后
解析时,您可以使用
RecursiveASTVisitor
遍历 AST 并在每个 CXXRecordDecl
上调用此方法。
完成此操作后,所有隐式成员都将可用。
Sema
对象?您无法从
ASTContext
获取它,因为那只是 AST,
而 Sema
是创建 AST 过程的一部分。 (如果你
如果将 AST 保存到磁盘然后再加载回来,就不会出现
Sema
根本就是物体。)
ASTUnit::LoadFromCompilerInvocation
进行初始解析。这会立即产生一个 ASTUnit
对象,
从中您可以获得 Sema
(通过 getSema()
)和
ASTContext
(通过getASTContext()
)。
ClangTool
,
这就是
教程
指向人们,你必须使用它的 buildASTs
方法而不是
run
。这可能需要对您的分析方式进行一些重构
但是,因为在这种情况下它不会调用 ToolAction
。