diff --git a/clang/include/clang/InstallAPI/Visitor.h b/clang/include/clang/InstallAPI/Visitor.h index 71d4d9894f420..546a2b85cb38c 100644 --- a/clang/include/clang/InstallAPI/Visitor.h +++ b/clang/include/clang/InstallAPI/Visitor.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_INSTALLAPI_VISITOR_H #define LLVM_CLANG_INSTALLAPI_VISITOR_H +#include "clang/AST/Availability.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/TargetInfo.h" @@ -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 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> 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 efc634d80dd21..c0d8526dae822 100644 --- a/clang/lib/InstallAPI/Frontend.cpp +++ b/clang/lib/InstallAPI/Frontend.cpp @@ -136,9 +136,9 @@ std::unique_ptr 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 1f2ef08e5aa25..89d753f8a0170 100644 --- a/clang/lib/InstallAPI/Visitor.cpp +++ b/clang/lib/InstallAPI/Visitor.cpp @@ -8,6 +8,7 @@ #include "clang/InstallAPI/Visitor.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 +19,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 +63,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 +287,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()) + 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(D.getDecl()); + if (const auto *Dtor = dyn_cast(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(D)) + GD = GlobalDecl(Ctor, CXXCtorType(Type)); + else { + const auto *Dtor = cast(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(Method) || !Method->isVirtual()) + continue; + + if (auto Dtor = dyn_cast(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(It.getType()->castAs()->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(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(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(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(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(D)) { + if (!Templ->isExplicitInstantiationOrSpecialization()) + return true; + } + + using var_iter = CXXRecordDecl::specific_decl_iterator; + using var_range = iterator_range; + 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() || 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 0000000000000..4817899095302 --- /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 Simple { +public: + virtual void foo() {} +}; +extern template class Simple; +} // 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 int foo1(T a) { return 1; } +template <> int foo1(int a); +extern template int foo1(short a); + +template int foo2(T a); + +// Partial specialization. +template class Partial { + static int run(A a, B b) { return a + b; } +}; + +template class Partial { + static int run(A a, int b) { return a - b; } +}; + +template class Foo { +public: + Foo(); + ~Foo(); +}; + +template class Bar { +public: + Bar(); + ~Bar() {} + + inline int bazinga() { return 7; } +}; + +extern template class Bar; + +class Bazz { +public: + Bazz() {} + + template int buzz(T a); + + float implicit() const { return foo1(0.0f); } +}; + +template int Bazz::buzz(T a) { return sizeof(T); } + +template struct S { static int x; }; + +template int S::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 b9c36eab2ad3b..701ab81c57c3d 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(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 f68addf197288..9d4d841284fd1 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;