[clang] [InstallAPI] Add support for C++ headers (PR #84403)
Cyndy Ishida via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 7 15:52:08 PST 2024
https://github.com/cyndyishida created https://github.com/llvm/llvm-project/pull/84403
This includes capturing symbols for global variables, functions, classes, and templated defintions. As pre-determing what symbols are generated from C++ declarations can be non-trivial, InstallAPI only parses select declarations for symbol generation when parsing c++.
For example, installapi only looks at explicit template instantiations or full template specializations, instead of general function or class templates, for symbol emittion.
>From 9068f8283df18ca8b3e39da67b9afa494847b6bf Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Mon, 4 Mar 2024 14:44:56 -0800
Subject: [PATCH] [InstallAPI] Add support for C++ headers
This includes capturing symbols for global variables, functions, classes, and templated defintions.
As pre-determing what symbols are generated from C++ declarations can be
non-trivial, InstallAPI only parses select declarations for symbol
generation when parsing c++.
For example, installapi only looks at explicit template instantiations
or full template specializations, instead of general function or class templates, for
symbol emittion.
---
clang/include/clang/InstallAPI/Visitor.h | 14 +
clang/lib/InstallAPI/Frontend.cpp | 4 +-
clang/lib/InstallAPI/Visitor.cpp | 426 +++++++++++++++++-
clang/test/InstallAPI/cpp.test | 530 +++++++++++++++++++++++
clang/tools/clang-installapi/Options.cpp | 33 +-
clang/tools/clang-installapi/Options.h | 7 +
6 files changed, 1008 insertions(+), 6 deletions(-)
create mode 100644 clang/test/InstallAPI/cpp.test
diff --git a/clang/include/clang/InstallAPI/Visitor.h b/clang/include/clang/InstallAPI/Visitor.h
index 71d4d9894f4205..9ac948ded3e332 100644
--- a/clang/include/clang/InstallAPI/Visitor.h
+++ b/clang/include/clang/InstallAPI/Visitor.h
@@ -21,6 +21,7 @@
#include "llvm/ADT/Twine.h"
namespace clang {
+struct AvailabilityInfo;
namespace installapi {
/// ASTVisitor for collecting declarations that represent global symbols.
@@ -33,6 +34,7 @@ class InstallAPIVisitor final : public ASTConsumer,
MC(ItaniumMangleContext::create(ASTCtx, ASTCtx.getDiagnostics())),
Layout(ASTCtx.getTargetInfo().getDataLayoutString()) {}
void HandleTranslationUnit(ASTContext &ASTCtx) override;
+ bool shouldVisitTemplateInstantiations() const { return true; }
/// Collect global variables.
bool VisitVarDecl(const VarDecl *D);
@@ -51,9 +53,19 @@ class InstallAPIVisitor final : public ASTConsumer,
/// is therefore itself not collected.
bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D);
+ /// Collect global c++ declarations.
+ bool VisitCXXRecordDecl(const CXXRecordDecl *D);
+
private:
std::string getMangledName(const NamedDecl *D) const;
std::string getBackendMangledName(llvm::Twine Name) const;
+ std::string getMangledCXXVTableName(const CXXRecordDecl *D) const;
+ std::string getMangledCXXThunk(const GlobalDecl &D,
+ const ThunkInfo &Thunk) const;
+ std::string getMangledCXXRTTI(const CXXRecordDecl *D) const;
+ std::string getMangledCXXRTTIName(const CXXRecordDecl *D) const;
+ std::string getMangledCtorDtor(const CXXMethodDecl *D, int Type) const;
+
std::optional<HeaderType> getAccessForDecl(const NamedDecl *D) const;
void recordObjCInstanceVariables(
const ASTContext &ASTCtx, llvm::MachO::ObjCContainerRecord *Record,
@@ -61,6 +73,8 @@ class InstallAPIVisitor final : public ASTConsumer,
const llvm::iterator_range<
DeclContext::specific_decl_iterator<ObjCIvarDecl>>
Ivars);
+ void emitVTableSymbols(const CXXRecordDecl *D, const AvailabilityInfo &Avail,
+ const HeaderType Access, bool EmittedVTable = false);
InstallAPIContext &Ctx;
SourceManager &SrcMgr;
diff --git a/clang/lib/InstallAPI/Frontend.cpp b/clang/lib/InstallAPI/Frontend.cpp
index 1edbdf5bb98360..0d526fe1da6667 100644
--- a/clang/lib/InstallAPI/Frontend.cpp
+++ b/clang/lib/InstallAPI/Frontend.cpp
@@ -137,9 +137,9 @@ std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
else
OS << "#import ";
if (H.useIncludeName())
- OS << "<" << H.getIncludeName() << ">";
+ OS << "<" << H.getIncludeName() << ">\n";
else
- OS << "\"" << H.getPath() << "\"";
+ OS << "\"" << H.getPath() << "\"\n";
Ctx.addKnownHeader(H);
}
diff --git a/clang/lib/InstallAPI/Visitor.cpp b/clang/lib/InstallAPI/Visitor.cpp
index 1f2ef08e5aa252..aded94f7a94a32 100644
--- a/clang/lib/InstallAPI/Visitor.cpp
+++ b/clang/lib/InstallAPI/Visitor.cpp
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//
#include "clang/InstallAPI/Visitor.h"
+#include "clang/AST/Availability.h"
#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Linkage.h"
#include "clang/InstallAPI/Frontend.h"
#include "llvm/ADT/SmallString.h"
@@ -18,6 +20,15 @@
using namespace llvm;
using namespace llvm::MachO;
+namespace {
+enum class CXXLinkage {
+ ExternalLinkage,
+ LinkOnceODRLinkage,
+ WeakODRLinkage,
+ PrivateLinkage,
+};
+}
+
namespace clang::installapi {
// Exported NamedDecl needs to have external linkage and
@@ -53,7 +64,7 @@ static bool isInlined(const FunctionDecl *D) {
return true;
}
-static SymbolFlags getFlags(bool WeakDef, bool ThreadLocal) {
+static SymbolFlags getFlags(bool WeakDef, bool ThreadLocal = false) {
SymbolFlags Result = SymbolFlags::None;
if (WeakDef)
Result |= SymbolFlags::WeakDefined;
@@ -277,8 +288,417 @@ bool InstallAPIVisitor::VisitFunctionDecl(const FunctionDecl *D) {
? RecordLinkage::Internal
: RecordLinkage::Exported;
Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail, D,
- *Access, getFlags(WeakDef, /*ThreadLocal=*/false),
- Inlined);
+ *Access, getFlags(WeakDef), Inlined);
+ return true;
+}
+
+static bool hasVTable(const CXXRecordDecl *D) {
+ // Check if vtable symbols should be emitted, only dynamic classes need
+ // vtables.
+ if (!D->hasDefinition() || !D->isDynamicClass())
+ return false;
+
+ assert(D->isExternallyVisible() && "Should be externally visible");
+ assert(D->isCompleteDefinition() && "Only works on complete definitions");
+
+ const CXXMethodDecl *KeyFunctionD =
+ D->getASTContext().getCurrentKeyFunction(D);
+ // If this class has a key function, then there is a vtable, possibly internal
+ // though.
+ if (KeyFunctionD) {
+ switch (KeyFunctionD->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ case TSK_ImplicitInstantiation:
+ case TSK_ExplicitInstantiationDefinition:
+ return true;
+ case TSK_ExplicitInstantiationDeclaration:
+ llvm_unreachable(
+ "Unexpected TemplateSpecializationKind for key function");
+ }
+ } else if (D->isAbstract()) {
+ // If the class is abstract and it doesn't have a key function, it is a
+ // 'pure' virtual class. It doesn't need a vtable.
+ return false;
+ }
+
+ switch (D->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ case TSK_ImplicitInstantiation:
+ return false;
+
+ case TSK_ExplicitInstantiationDeclaration:
+ case TSK_ExplicitInstantiationDefinition:
+ return true;
+ }
+
+ llvm_unreachable("Invalid TemplateSpecializationKind!");
+}
+
+static CXXLinkage getVTableLinkage(const CXXRecordDecl *D) {
+ assert((D->hasDefinition() && D->isDynamicClass()) && "Record has no vtable");
+ assert(D->isExternallyVisible() && "Record should be externally visible");
+ if (D->getVisibility() == HiddenVisibility)
+ return CXXLinkage::PrivateLinkage;
+
+ const CXXMethodDecl *KeyFunctionD =
+ D->getASTContext().getCurrentKeyFunction(D);
+ if (KeyFunctionD) {
+ // If this class has a key function, use that to determine the
+ // linkage of the vtable.
+ switch (KeyFunctionD->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ if (isInlined(KeyFunctionD))
+ return CXXLinkage::LinkOnceODRLinkage;
+ return CXXLinkage::ExternalLinkage;
+ case TSK_ImplicitInstantiation:
+ llvm_unreachable("No external vtable for implicit instantiations");
+ case TSK_ExplicitInstantiationDefinition:
+ return CXXLinkage::WeakODRLinkage;
+ case TSK_ExplicitInstantiationDeclaration:
+ llvm_unreachable(
+ "Unexpected TemplateSpecializationKind for key function");
+ }
+ }
+
+ switch (D->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ case TSK_ImplicitInstantiation:
+ return CXXLinkage::LinkOnceODRLinkage;
+ case TSK_ExplicitInstantiationDeclaration:
+ case TSK_ExplicitInstantiationDefinition:
+ return CXXLinkage::WeakODRLinkage;
+ }
+
+ llvm_unreachable("Invalid TemplateSpecializationKind!");
+}
+
+static bool isRTTIWeakDef(const CXXRecordDecl *D) {
+ if (D->hasAttr<WeakAttr>())
+ return true;
+
+ if (D->isAbstract() && D->getASTContext().getCurrentKeyFunction(D) == nullptr)
+ return true;
+
+ if (D->isDynamicClass())
+ return getVTableLinkage(D) != CXXLinkage::ExternalLinkage;
+
+ return false;
+}
+
+static bool hasRTTI(const CXXRecordDecl *D) {
+ if (!D->getASTContext().getLangOpts().RTTI)
+ return false;
+
+ if (!D->hasDefinition())
+ return false;
+
+ if (!D->isDynamicClass())
+ return false;
+
+ // Don't emit weak-def RTTI information. InstallAPI cannot reliably determine
+ // if the final binary will have those weak defined RTTI symbols. This depends
+ // on the optimization level and if the class has been instantiated and used.
+ //
+ // Luckily, the Apple static linker doesn't need those weak defined RTTI
+ // symbols for linking. They are only needed by the runtime linker. That means
+ // they can be safely dropped.
+ if (isRTTIWeakDef(D))
+ return false;
+
+ return true;
+}
+
+std::string
+InstallAPIVisitor::getMangledCXXRTTIName(const CXXRecordDecl *D) const {
+ SmallString<256> Name;
+ raw_svector_ostream NameStream(Name);
+ MC->mangleCXXRTTIName(QualType(D->getTypeForDecl(), 0), NameStream);
+
+ return getBackendMangledName(Name);
+}
+
+std::string InstallAPIVisitor::getMangledCXXRTTI(const CXXRecordDecl *D) const {
+ SmallString<256> Name;
+ raw_svector_ostream NameStream(Name);
+ MC->mangleCXXRTTI(QualType(D->getTypeForDecl(), 0), NameStream);
+
+ return getBackendMangledName(Name);
+}
+
+std::string
+InstallAPIVisitor::getMangledCXXVTableName(const CXXRecordDecl *D) const {
+ SmallString<256> Name;
+ raw_svector_ostream NameStream(Name);
+ MC->mangleCXXVTable(D, NameStream);
+
+ return getBackendMangledName(Name);
+}
+
+std::string
+InstallAPIVisitor::getMangledCXXThunk(const GlobalDecl &D,
+ const ThunkInfo &Thunk) const {
+ SmallString<256> Name;
+ raw_svector_ostream NameStream(Name);
+ const auto *Method = cast<CXXMethodDecl>(D.getDecl());
+ if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method))
+ MC->mangleCXXDtorThunk(Dtor, D.getDtorType(), Thunk.This, NameStream);
+ else
+ MC->mangleThunk(Method, Thunk, NameStream);
+
+ return getBackendMangledName(Name);
+}
+
+std::string InstallAPIVisitor::getMangledCtorDtor(const CXXMethodDecl *D,
+ int Type) const {
+ SmallString<256> Name;
+ raw_svector_ostream NameStream(Name);
+ GlobalDecl GD;
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
+ GD = GlobalDecl(Ctor, CXXCtorType(Type));
+ else {
+ const auto *Dtor = cast<CXXDestructorDecl>(D);
+ GD = GlobalDecl(Dtor, CXXDtorType(Type));
+ }
+ MC->mangleName(GD, NameStream);
+ return getBackendMangledName(Name);
+}
+
+void InstallAPIVisitor::emitVTableSymbols(const CXXRecordDecl *D,
+ const AvailabilityInfo &Avail,
+ const HeaderType Access,
+ bool EmittedVTable) {
+ if (hasVTable(D)) {
+ EmittedVTable = true;
+ const CXXLinkage VTableLinkage = getVTableLinkage(D);
+ if (VTableLinkage == CXXLinkage::ExternalLinkage ||
+ VTableLinkage == CXXLinkage::WeakODRLinkage) {
+ const std::string Name = getMangledCXXVTableName(D);
+ const bool WeakDef = VTableLinkage == CXXLinkage::WeakODRLinkage;
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail, D, Access,
+ getFlags(WeakDef));
+ if (!D->getDescribedClassTemplate() && !D->isInvalidDecl()) {
+ VTableContextBase *VTable = D->getASTContext().getVTableContext();
+ auto AddThunk = [&](GlobalDecl GD) {
+ const ItaniumVTableContext::ThunkInfoVectorTy *Thunks =
+ VTable->getThunkInfo(GD);
+ if (!Thunks)
+ return;
+
+ for (const auto &Thunk : *Thunks) {
+ const std::string Name = getMangledCXXThunk(GD, Thunk);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail,
+ GD.getDecl(), Access);
+ }
+ };
+
+ for (const auto *Method : D->methods()) {
+ if (isa<CXXConstructorDecl>(Method) || !Method->isVirtual())
+ continue;
+
+ if (auto Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
+ // Skip default destructor.
+ if (Dtor->isDefaulted())
+ continue;
+ AddThunk({Dtor, Dtor_Deleting});
+ AddThunk({Dtor, Dtor_Complete});
+ } else
+ AddThunk(Method);
+ }
+ }
+ }
+ }
+
+ if (!EmittedVTable)
+ return;
+
+ if (hasRTTI(D)) {
+ std::string Name = getMangledCXXRTTI(D);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail, D, Access);
+
+ Name = getMangledCXXRTTIName(D);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail, D, Access);
+ }
+
+ for (const auto &It : D->bases()) {
+ const CXXRecordDecl *Base =
+ cast<CXXRecordDecl>(It.getType()->castAs<RecordType>()->getDecl());
+ const auto BaseAccess = getAccessForDecl(Base);
+ if (!BaseAccess)
+ continue;
+ const AvailabilityInfo BaseAvail = AvailabilityInfo::createFromDecl(Base);
+ emitVTableSymbols(Base, BaseAvail, *BaseAccess, /*EmittedVTable=*/true);
+ }
+}
+
+bool InstallAPIVisitor::VisitCXXRecordDecl(const CXXRecordDecl *D) {
+ if (!D->isCompleteDefinition())
+ return true;
+
+ // Skip templated classes.
+ if (D->getDescribedClassTemplate() != nullptr)
+ return true;
+
+ // Skip partial templated classes too.
+ if (isa<ClassTemplatePartialSpecializationDecl>(D))
+ return true;
+
+ auto Access = getAccessForDecl(D);
+ if (!Access)
+ return true;
+ const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
+
+ // Check whether to emit the vtable/rtti symbols.
+ if (isExported(D))
+ emitVTableSymbols(D, Avail, *Access);
+
+ TemplateSpecializationKind ClassSK = TSK_Undeclared;
+ bool KeepInlineAsWeak = false;
+ if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
+ ClassSK = Templ->getTemplateSpecializationKind();
+ if (ClassSK == TSK_ExplicitInstantiationDeclaration)
+ KeepInlineAsWeak = true;
+ }
+
+ // Record the class methods.
+ for (const auto *M : D->methods()) {
+ // Inlined methods are usually not emitted, except when it comes from a
+ // specialized template.
+ bool WeakDef = false;
+ if (isInlined(M)) {
+ if (!KeepInlineAsWeak)
+ continue;
+
+ WeakDef = true;
+ }
+
+ if (!isExported(M))
+ continue;
+
+ switch (M->getTemplateSpecializationKind()) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ break;
+ case TSK_ImplicitInstantiation:
+ continue;
+ case TSK_ExplicitInstantiationDeclaration:
+ if (ClassSK == TSK_ExplicitInstantiationDeclaration)
+ WeakDef = true;
+ break;
+ case TSK_ExplicitInstantiationDefinition:
+ WeakDef = true;
+ break;
+ }
+
+ if (!M->isUserProvided())
+ continue;
+
+ // Methods that are deleted are not exported.
+ if (M->isDeleted())
+ continue;
+
+ const auto Access = getAccessForDecl(M);
+ if (!Access)
+ return true;
+ const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(M);
+
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(M)) {
+ // Defaulted constructors are not exported.
+ if (Ctor->isDefaulted())
+ continue;
+
+ std::string Name = getMangledCtorDtor(M, Ctor_Base);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail, D, *Access,
+ getFlags(WeakDef));
+
+ if (!D->isAbstract()) {
+ std::string Name = getMangledCtorDtor(M, Ctor_Complete);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail, D, *Access,
+ getFlags(WeakDef));
+ }
+
+ continue;
+ }
+
+ if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(M)) {
+ // Defaulted destructors are not exported.
+ if (Dtor->isDefaulted())
+ continue;
+
+ std::string Name = getMangledCtorDtor(M, Dtor_Base);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail, D, *Access,
+ getFlags(WeakDef));
+
+ Name = getMangledCtorDtor(M, Dtor_Complete);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail, D, *Access,
+ getFlags(WeakDef));
+
+ if (Dtor->isVirtual()) {
+ Name = getMangledCtorDtor(M, Dtor_Deleting);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail, D, *Access,
+ getFlags(WeakDef));
+ }
+
+ continue;
+ }
+
+ // Though abstract methods can map to exports, this is generally unexpected.
+ // Except in the case of destructors. Only ignore pure virtuals after
+ // checking if the member function was a destructor.
+ if (M->isPureVirtual())
+ continue;
+
+ std::string Name = getMangledName(M);
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Function, Avail, D, *Access,
+ getFlags(WeakDef));
+ }
+
+ if (auto *Templ = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
+ if (!Templ->isExplicitInstantiationOrSpecialization())
+ return true;
+ }
+
+ using var_iter = CXXRecordDecl::specific_decl_iterator<VarDecl>;
+ using var_range = iterator_range<var_iter>;
+ for (const auto *Var : var_range(D->decls())) {
+ // Skip const static member variables.
+ // \code
+ // struct S {
+ // static const int x = 0;
+ // };
+ // \endcode
+ if (Var->isStaticDataMember() && Var->hasInit())
+ continue;
+
+ // Skip unexported var decls.
+ if (!isExported(Var))
+ continue;
+
+ const std::string Name = getMangledName(Var);
+ const auto Access = getAccessForDecl(Var);
+ if (!Access)
+ return true;
+ const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(Var);
+ const bool WeakDef = Var->hasAttr<WeakAttr>() || KeepInlineAsWeak;
+
+ Ctx.Slice->addGlobal(Name, RecordLinkage::Exported,
+ GlobalRecord::Kind::Variable, Avail, D, *Access,
+ getFlags(WeakDef));
+ }
+
return true;
}
diff --git a/clang/test/InstallAPI/cpp.test b/clang/test/InstallAPI/cpp.test
new file mode 100644
index 00000000000000..4817899095302b
--- /dev/null
+++ b/clang/test/InstallAPI/cpp.test
@@ -0,0 +1,530 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json
+
+// Invoke C++ with no-rtti.
+// RUN: clang-installapi -target arm64-apple-macos13.1 \
+// RUN: -I%t/usr/include -I%t/usr/local/include -x c++ \
+// RUN: -install_name @rpath/lib/libcpp.dylib -fno-rtti \
+// RUN: %t/inputs.json -o %t/no-rtti.tbd 2>&1 | FileCheck %s --allow-empty
+
+// RUN: llvm-readtapi -compare %t/no-rtti.tbd \
+// RUN: %t/expected-no-rtti.tbd 2>&1 | FileCheck %s --allow-empty
+
+// Invoke C++ with rtti.
+// RUN: clang-installapi -target arm64-apple-macos13.1 \
+// RUN: -I%t/usr/include -I%t/usr/local/include -x c++ \
+// RUN: -install_name @rpath/lib/libcpp.dylib -frtti \
+// RUN: %t/inputs.json -o %t/rtti.tbd 2>&1 | FileCheck %s --allow-empty
+// RUN: llvm-readtapi -compare %t/rtti.tbd \
+// RUN: %t/expected-rtti.tbd 2>&1 | FileCheck %s --allow-empty
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- usr/include/basic.h
+#ifndef CPP_H
+#define CPP_H
+
+inline int foo(int x) { return x + 1; }
+
+extern int bar(int x) { return x + 1; }
+
+inline int baz(int x) {
+ static const int a[] = {1, 2, 3};
+ return a[x];
+}
+
+extern "C" {
+ int cFunc(const char*);
+}
+
+class Bar {
+public:
+ static const int x = 0;
+ static int y;
+
+ inline int func1(int x) { return x + 2; }
+ inline int func2(int x);
+ int func3(int x);
+};
+
+class __attribute__((visibility("hidden"))) BarI {
+ static const int x = 0;
+ static int y;
+
+ inline int func1(int x) { return x + 2; }
+ inline int func2(int x);
+ int func3(int x);
+};
+
+int Bar::func2(int x) { return x + 3; }
+inline int Bar::func3(int x) { return x + 4; }
+
+int BarI::func2(int x) { return x + 3; }
+inline int BarI::func3(int x) { return x + 4; }
+#endif
+
+//--- usr/local/include/vtable.h
+// Simple test class with no virtual functions. There should be no vtable or
+// RTTI.
+namespace test1 {
+class Simple {
+public:
+ void run();
+};
+} // end namespace test1
+
+// Simple test class with virtual function. There should be an external vtable
+// and RTTI.
+namespace test2 {
+class Simple {
+public:
+ virtual void run();
+};
+} // end namespace test2
+
+// Abstract class with no sub classes. There should be no vtable or RTTI.
+namespace test3 {
+class Abstract {
+public:
+ virtual ~Abstract() {}
+ virtual void run() = 0;
+};
+} // end namespace test3
+
+// Abstract base class with a sub class. There should be weak-def RTTI for the
+// abstract base class.
+// The sub-class should have vtable and RTTI.
+namespace test4 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run() = 0;
+};
+
+class Sub : public Base {
+public:
+ void run() override;
+};
+} // end namespace test4
+
+// Abstract base class with a sub class. Same as above, but with a user defined
+// inlined destructor.
+namespace test5 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run() = 0;
+};
+
+class Sub : public Base {
+public:
+ virtual ~Sub() {}
+ void run() override;
+};
+} // end namespace test5
+
+// Abstract base class with a sub class. Same as above, but with a different
+// inlined key method.
+namespace test6 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run() = 0;
+};
+
+class Sub : public Base {
+public:
+ virtual void foo() {}
+ void run() override;
+};
+} // end namespace test6
+
+// Abstract base class with a sub class. Overloaded method is implemented
+// inline. No vtable or RTTI.
+namespace test7 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual bool run() = 0;
+};
+
+class Sub : public Base {
+public:
+ bool run() override { return true; }
+};
+} // end namespace test7
+
+// Abstract base class with a sub class. Overloaded method has no inline
+// attribute and is recognized as key method,
+// but is later implemented inline. Weak-def RTTI only.
+namespace test8 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run() = 0;
+};
+
+class Sub : public Base {
+public:
+ void run() override;
+};
+
+inline void Sub::run() {}
+} // end namespace test8
+
+namespace test9 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run1() = 0;
+ virtual void run2() = 0;
+};
+
+class Sub : public Base {
+public:
+ void run1() override {}
+ void run2() override;
+};
+
+inline void Sub::run2() {}
+} // end namespace test9
+
+namespace test10 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run1() = 0;
+ virtual void run2() = 0;
+};
+
+class Sub : public Base {
+public:
+ void run1() override {}
+ inline void run2() override;
+};
+
+void Sub::run2() {}
+} // end namespace test10
+
+namespace test11 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run1() = 0;
+ virtual void run2() = 0;
+ virtual void run3() = 0;
+};
+
+class Sub : public Base {
+public:
+ void run1() override {}
+ void run2() override;
+ void run3() override;
+};
+
+inline void Sub::run2() {}
+} // end namespace test11
+
+namespace test12 {
+template <class T> class Simple {
+public:
+ virtual void foo() {}
+};
+extern template class Simple<int>;
+} // end namespace test12
+
+namespace test13 {
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run1() = 0;
+ virtual void run2() {};
+ virtual void run3(); // key function.
+};
+
+class Sub : public Base {
+public:
+ void run1() override {}
+ void run2() override {}
+};
+
+} // end namespace test13
+
+namespace test14 {
+
+class __attribute__((visibility("hidden"))) Base
+{
+public:
+ Base() {}
+ virtual ~Base(); // keyfunction.
+ virtual void run1() const = 0;
+};
+
+class Sub : public Base
+{
+public:
+ Sub();
+ virtual ~Sub();
+ virtual void run1() const;
+ void run2() const {}
+};
+
+} // end namespace test14
+
+namespace test15 {
+
+class Base {
+public:
+ virtual ~Base() {}
+ virtual void run() {};
+};
+
+class Base1 {
+public:
+ virtual ~Base1() {}
+ virtual void run1() {};
+};
+
+class Sub : public Base, public Base1 {
+public:
+ Sub() {}
+ ~Sub();
+ void run() override;
+ void run1() override;
+};
+
+class Sub1 : public Base, public Base1 {
+public:
+ Sub1() {}
+ ~Sub1() = default;
+ void run() override;
+ void run1() override;
+};
+
+} // end namespace test15
+
+//--- usr/local/include/templates.h
+#ifndef TEMPLATES_H
+#define TEMPLATES_H
+
+namespace templates {
+
+// Full specialization.
+template <class T> int foo1(T a) { return 1; }
+template <> int foo1<int>(int a);
+extern template int foo1<short>(short a);
+
+template <class T> int foo2(T a);
+
+// Partial specialization.
+template <class A, class B> class Partial {
+ static int run(A a, B b) { return a + b; }
+};
+
+template <class A> class Partial<A, int> {
+ static int run(A a, int b) { return a - b; }
+};
+
+template <class T> class Foo {
+public:
+ Foo();
+ ~Foo();
+};
+
+template <class T> class Bar {
+public:
+ Bar();
+ ~Bar() {}
+
+ inline int bazinga() { return 7; }
+};
+
+extern template class Bar<int>;
+
+class Bazz {
+public:
+ Bazz() {}
+
+ template <class T> int buzz(T a);
+
+ float implicit() const { return foo1(0.0f); }
+};
+
+template <class T> int Bazz::buzz(T a) { return sizeof(T); }
+
+template <class T> struct S { static int x; };
+
+template <class T> int S<T>::x = 0;
+
+} // end namespace templates.
+
+#endif
+
+
+//--- inputs.json.in
+{
+ "headers": [ {
+ "path" : "DSTROOT/usr/include/basic.h",
+ "type" : "public"
+ },
+ {
+ "path" : "DSTROOT/usr/local/include/vtable.h",
+ "type" : "private"
+ },
+ {
+ "path" : "DSTROOT/usr/local/include/templates.h",
+ "type" : "private"
+ }
+ ],
+ "version": "3"
+}
+
+//--- expected-no-rtti.tbd
+{
+ "main_library": {
+ "compatibility_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "current_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "exported_symbols": [
+ {
+ "data": {
+ "global": [
+ "__ZTVN6test143SubE", "__ZTVN6test113SubE", "__ZTVN5test26SimpleE",
+ "__ZTVN5test53SubE", "__ZTVN6test154Sub1E", "__ZTVN6test153SubE",
+ "__ZN3Bar1yE", "__ZTVN5test43SubE", "__ZTVN5test63SubE",
+ "__ZTVN6test134BaseE"
+ ],
+ "weak": [
+ "__ZTVN6test126SimpleIiEE"
+ ]
+ },
+ "text": {
+ "global": [
+ "__ZN6test153Sub3runEv", "__ZN6test154Sub13runEv",
+ "__Z3bari", "__ZThn8_N6test153SubD1Ev",
+ "__ZNK6test143Sub4run1Ev", "__ZN6test154Sub14run1Ev",
+ "__ZThn8_N6test153Sub4run1Ev", "__ZN6test143SubD1Ev",
+ "__ZN6test134Base4run3Ev", "__ZN5test16Simple3runEv",
+ "__ZN5test43Sub3runEv", "__ZN6test113Sub4run3Ev", "__ZN6test153SubD2Ev",
+ "__ZN5test53Sub3runEv", "__ZN6test153SubD1Ev", "__ZN6test143SubC1Ev",
+ "__ZN9templates4foo1IiEEiT_", "__ZN6test143SubC2Ev", "__ZN5test63Sub3runEv",
+ "__ZN5test26Simple3runEv", "__ZN6test153SubD0Ev",
+ "__ZN6test143SubD2Ev", "__ZN6test153Sub4run1Ev", "__ZN6test143SubD0Ev",
+ "__ZThn8_N6test153SubD0Ev", "__ZThn8_N6test154Sub14run1Ev", "_cFunc"
+ ],
+ "weak": [
+ "__ZN9templates3BarIiED2Ev", "__ZN9templates3BarIiEC2Ev",
+ "__ZN9templates3BarIiEC1Ev", "__ZN9templates3BarIiED1Ev",
+ "__ZN6test126SimpleIiE3fooEv", "__ZN9templates3BarIiE7bazingaEv",
+ "__ZN9templates4foo1IsEEiT_"
+ ]
+ }
+ }
+ ],
+ "flags": [
+ {
+ "attributes": [
+ "not_app_extension_safe"
+ ]
+ }
+ ],
+ "install_names": [
+ {
+ "name": "@rpath/lib/libcpp.dylib"
+ }
+ ],
+ "target_info": [
+ {
+ "min_deployment": "13.1",
+ "target": "arm64-macos"
+ }
+ ]
+ },
+ "tapi_tbd_version": 5
+}
+
+//--- expected-rtti.tbd
+{
+ "main_library": {
+ "compatibility_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "current_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "exported_symbols": [
+ {
+ "data": {
+ "global": [
+ "__ZTVN6test143SubE", "__ZTIN5test63SubE", "__ZTSN5test26SimpleE",
+ "__ZTIN6test153SubE", "__ZTVN6test113SubE", "__ZTIN5test43SubE",
+ "__ZTIN6test134BaseE", "__ZTVN5test26SimpleE", "__ZTIN5test26SimpleE",
+ "__ZTSN6test134BaseE", "__ZTVN6test154Sub1E", "__ZTVN5test43SubE",
+ "__ZTVN5test63SubE", "__ZTSN5test43SubE", "__ZTSN6test113SubE",
+ "__ZTIN6test154Sub1E", "__ZTSN6test153SubE", "__ZTSN5test63SubE",
+ "__ZTSN6test154Sub1E", "__ZTIN6test113SubE", "__ZTSN6test143SubE",
+ "__ZTVN5test53SubE", "__ZTIN6test143SubE", "__ZTVN6test153SubE",
+ "__ZTIN5test53SubE", "__ZN3Bar1yE", "__ZTVN6test134BaseE",
+ "__ZTSN5test53SubE"
+ ],
+ "weak": [
+ "__ZTVN6test126SimpleIiEE"
+ ]
+ },
+ "text": {
+ "global": [
+ "__ZN6test154Sub13runEv", "__ZN6test153Sub3runEv", "__ZNK6test143Sub4run1Ev",
+ "__ZN6test134Base4run3Ev", "__ZN5test16Simple3runEv", "__ZN6test153SubD2Ev",
+ "__ZN6test143SubC2Ev", "__ZN5test63Sub3runEv", "__ZN6test153SubD0Ev",
+ "__ZN6test143SubD2Ev", "__ZThn8_N6test154Sub14run1Ev",
+ "__ZThn8_N6test153SubD0Ev", "__Z3bari", "__ZThn8_N6test153SubD1Ev",
+ "__ZN6test154Sub14run1Ev", "__ZThn8_N6test153Sub4run1Ev",
+ "__ZN6test143SubD1Ev", "__ZN5test43Sub3runEv",
+ "__ZN6test113Sub4run3Ev", "__ZN5test53Sub3runEv", "__ZN6test143SubC1Ev",
+ "__ZN6test153SubD1Ev", "__ZN9templates4foo1IiEEiT_", "__ZN5test26Simple3runEv",
+ "__ZN6test153Sub4run1Ev", "__ZN6test143SubD0Ev", "_cFunc"
+ ],
+ "weak": [
+ "__ZN9templates3BarIiEC2Ev", "__ZN9templates3BarIiEC1Ev",
+ "__ZN9templates3BarIiED1Ev", "__ZN6test126SimpleIiE3fooEv",
+ "__ZN9templates4foo1IsEEiT_", "__ZN9templates3BarIiED2Ev",
+ "__ZN9templates3BarIiE7bazingaEv"
+ ]
+ }
+ }
+ ],
+ "flags": [
+ {
+ "attributes": [
+ "not_app_extension_safe"
+ ]
+ }
+ ],
+ "install_names": [
+ {
+ "name": "@rpath/lib/libcpp.dylib"
+ }
+ ],
+ "target_info": [
+ {
+ "min_deployment": "13.1",
+ "target": "arm64-macos"
+ }
+ ]
+ },
+ "tapi_tbd_version": 5
+}
+
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index b9c36eab2ad3b7..701ab81c57c3d0 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -99,6 +99,33 @@ bool Options::processLinkerOptions(InputArgList &Args) {
return true;
}
+bool Options::processFrontendOptions(InputArgList &Args) {
+ // Do not claim any arguments, as they will be passed along for CC1
+ // invocations.
+ if (auto *A = Args.getLastArgNoClaim(OPT_x)) {
+ FEOpts.LangMode = llvm::StringSwitch<clang::Language>(A->getValue())
+ .Case("c", clang::Language::C)
+ .Case("c++", clang::Language::CXX)
+ .Case("objective-c", clang::Language::ObjC)
+ .Case("objective-c++", clang::Language::ObjCXX)
+ .Default(clang::Language::Unknown);
+
+ if (FEOpts.LangMode == clang::Language::Unknown) {
+ Diags->Report(clang::diag::err_drv_invalid_value)
+ << A->getAsString(Args) << A->getValue();
+ return false;
+ }
+ }
+ for (auto *A : Args.filtered(OPT_ObjC, OPT_ObjCXX)) {
+ if (A->getOption().matches(OPT_ObjC))
+ FEOpts.LangMode = clang::Language::ObjC;
+ else
+ FEOpts.LangMode = clang::Language::ObjCXX;
+ }
+
+ return true;
+}
+
Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
InputArgList &ArgList)
: Diags(&Diag), FM(FM) {
@@ -108,7 +135,10 @@ Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
if (!processLinkerOptions(ArgList))
return;
- /// Any remaining arguments should be handled by invoking the clang frontend.
+ if (!processFrontendOptions(ArgList))
+ return;
+
+ /// Any unclaimed arguments should be handled by invoking the clang frontend.
for (const Arg *A : ArgList) {
if (A->isClaimed())
continue;
@@ -132,6 +162,7 @@ InstallAPIContext Options::createContext() {
Ctx.BA.AppExtensionSafe = LinkerOpts.AppExtensionSafe;
Ctx.FT = DriverOpts.OutFT;
Ctx.OutputLoc = DriverOpts.OutputPath;
+ Ctx.LangMode = FEOpts.LangMode;
// Process inputs.
for (const std::string &ListPath : DriverOpts.FileLists) {
diff --git a/clang/tools/clang-installapi/Options.h b/clang/tools/clang-installapi/Options.h
index f68addf1972880..9d4d841284fd11 100644
--- a/clang/tools/clang-installapi/Options.h
+++ b/clang/tools/clang-installapi/Options.h
@@ -62,15 +62,22 @@ struct LinkerOptions {
bool IsDylib = false;
};
+struct FrontendOptions {
+ /// \brief The language mode to parse headers in.
+ Language LangMode = Language::ObjC;
+};
+
class Options {
private:
bool processDriverOptions(llvm::opt::InputArgList &Args);
bool processLinkerOptions(llvm::opt::InputArgList &Args);
+ bool processFrontendOptions(llvm::opt::InputArgList &Args);
public:
/// The various options grouped together.
DriverOptions DriverOpts;
LinkerOptions LinkerOpts;
+ FrontendOptions FEOpts;
Options() = delete;
More information about the cfe-commits
mailing list