提问人:GKxx 提问时间:9/12/2023 最后编辑:Scott McPeakGKxx 更新时间:9/23/2023 访问量:64
关于 clang AST 和 odr - 析构函数的使用
About clang AST and odr-use of destructors
问:
对于以下代码
struct X {
int a;
};
int main() {
X x;
return 0;
}
叮当声 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使用的析构函数呢?这个 odr 的析构函数是否使用过?或者这只是关于我对 clang AST 和行为的误解?X
clang::CXXRecordDecl::getDestructor
请注意,相比之下,clang-AST 显示 的默认构造函数已定义,即使它不执行任何操作。X
答:
是否使用了此析构函数 ODR?
是的。引用 basic.def.odr:
如果可能调用类的析构函数,则使用 odr。
其中“潜在调用”有一个不平凡的定义,但简而言之,
在这里为 true,因为声明了一个 类型的对象。main()
X
Clang 是什么意思?getDestructor()
nullptr
CXXRecordDecl::getDestructor()
的文档只是说:
返回此类的析构函数 decl。
甚至不承认它可以返回,更不用说了
解释这意味着什么。根据阅读源码,我
得出结论,这意味着析构函数是微不足道的,并且没有一个
无论如何都会导致创建隐式声明的条件
(见下文)已经满意了。nullptr
这并不意味着 Clang 声称析构函数不是
ODR 使用。Decl::isUsed()
方法声称可以报告此信息(参见 上的文档),但它似乎并不完全准确;甚至
如果我通过添加函数来创建析构函数声明,则析构函数仍然没有被标记。setIsUsed()
virtual
isUsed()
标准不是说应该在这里定义析构函数吗?
是的,但为了符合要求,Clang 只需要生成编译的输出 它的行为“就好像”析构函数已被定义一样。缺少 特定的 AST 节点不会使其不符合要求。即使缺乏 编译对象文件中的定义不会,只要它遵循 相关的 ABI,从而与其他工具合作,再次实现 所需的“仿佛”行为(假设源代码 符合一个定义规则)。
Clang 创建隐式声明的条件是什么?
这似乎没有记录在案,所以我试图从
来源。隐式析构函数声明由 SemaDeclCXX.cpp:13803
创建。
这个函数在几个地方被调用,我没有遵循所有
链向后,但对于第一个近似值,这不会是
如果出现以下情况,则称为:Sema::DeclareImplicitDestructor()
- 析构函数本身是微不足道的,并且
- 该类没有 vtable,并且
- 析构函数之间不需要过载解决方案(这可能
需要 )。
requires
请注意,这是最后一个条件,由于
的声明,这导致 Clang 声明三个隐式结构函数。x
我是否可以强制声明析构函数?
是的!您可以在 a 上调用 Sema::ForceDeclarationOfImplicitMembers
来强制 Clang 声明隐式成员,甚至
否则它不会这样做。例如,紧接着
解析时,可以使用 RecursiveASTVisitor
遍历 AST 并在每个 .CXXRecordDecl
CXXRecordDecl
完成此操作后,所有隐式成员都将可用。
我将如何获得对象?Sema
你不能从 中得到它,因为那只是 AST,
while 是创建 AST 过程的一部分。(如果你
如果将 AST 保存到磁盘,然后将其加载回去,则根本不会有对象。ASTContext
Sema
Sema
我的首选方法是使用 ASTUnit::LoadFromCompilerInvocation
进行初始解析。这会立即产生一个对象,
您可以从中获取 (via ) 和 (via )。ASTUnit
Sema
getSema()
ASTContext
getASTContext()
如果您改用 ClangTool::run
,
这就是本教程指向人们的内容:
Tool.run(
clang::tooling::newFrontendActionFactory<MyASTFrontendAction>().get());
抓住 :CompilerInstance
CreateASTConsumer
class MyASTFrontendAction : public clang::ASTFrontendAction {
public:
virtual unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &ci,
llvm::StringRef inFile) override
{
return make_unique<MyASTConsumer>(ci);
}
};
然后将其存储在 中,并在需要时使用它:ASTConsumer
getSema()
class MyASTConsumer : public clang::ASTConsumer {
public:
clang::CompilerInstance &m_compilerInstance;
MyASTConsumer(clang::CompilerInstance &compilerInstance)
: m_compilerInstance(compilerInstance)
{}
virtual void HandleTranslationUnit(clang::ASTContext &Context) override
{
clang::Sema &sema = m_compilerInstance.getSema();
// ...
}
};
评论
The documentation of CXXRecordDecl::getDestructor() just says "Returns the destructor decl for this class." without even acknowledging that it can return nullptr, let alone explaining what that would mean.
getSema()
CompilerInstance
ASTFrontendAction::CreateASTConsumer
Sema::ForceDeclarationOfImplicitMembers
Sema::DefineImplicitxxx
Sema::getUndefinedButUsed()
CompilerInstance
Decl::isUsed()
下一个:对象实例在销毁后仍可访问
评论