From bfdcdc8e26097c9dbb4c40d78296f6ccc3e6684c Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 15 Dec 2010 04:00:32 +0000 Subject: [PATCH] Set the "implicitly inline" bit on a method as soon as we see a definition within the class. Teach IR gen to look for function definitions in record lexical contexts when deciding whether to emit a function whose address was taken. Fixes PR8789. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@121833 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 7 ++++- lib/CodeGen/CodeGenModule.cpp | 47 ++++++++++++++-------------- lib/Sema/SemaDecl.cpp | 6 ++++ test/CodeGenCXX/inline-functions.cpp | 24 ++++++++++++++ 4 files changed, 60 insertions(+), 24 deletions(-) diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index eb260e3aa8..2c0a02f0e6 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1503,11 +1503,16 @@ public: IsInline = I; } + /// Flag that this function is implicitly inline. + void setImplicitlyInline() { + IsInline = true; + } + /// \brief Determine whether this function should be inlined, because it is /// either marked "inline" or is a member function of a C++ class that /// was defined in the class body. bool isInlined() const; - + bool isInlineDefinitionExternallyVisible() const; /// isOverloadedOperator - Whether this function declaration diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 1dab027455..142f29820e 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -829,30 +829,31 @@ CodeGenModule::GetOrCreateLLVMFunction(llvm::StringRef MangledName, // list, and remove it from DeferredDecls (since we don't need it anymore). DeferredDeclsToEmit.push_back(DDI->second); DeferredDecls.erase(DDI); - } else if (const FunctionDecl *FD = cast_or_null(D.getDecl())) { - // If this the first reference to a C++ inline function in a class, queue up - // the deferred function body for emission. These are not seen as - // top-level declarations. - if (FD->isThisDeclarationADefinition() && MayDeferGeneration(FD)) { - DeferredDeclsToEmit.push_back(D); - // A called constructor which has no definition or declaration need be - // synthesized. - } else if (const CXXConstructorDecl *CD = dyn_cast(FD)) { - if (CD->isImplicit()) { - assert(CD->isUsed() && "Sema doesn't consider constructor as used."); - DeferredDeclsToEmit.push_back(D); - } - } else if (const CXXDestructorDecl *DD = dyn_cast(FD)) { - if (DD->isImplicit()) { - assert(DD->isUsed() && "Sema doesn't consider destructor as used."); - DeferredDeclsToEmit.push_back(D); - } - } else if (const CXXMethodDecl *MD = dyn_cast(FD)) { - if (MD->isImplicit() && MD->isCopyAssignmentOperator()) { - assert(MD->isUsed() && "Sema doesn't consider CopyAssignment as used."); - DeferredDeclsToEmit.push_back(D); + + // Otherwise, there are cases we have to worry about where we're + // using a declaration for which we must emit a definition but where + // we might not find a top-level definition: + // - member functions defined inline in their classes + // - friend functions defined inline in some class + // - special member functions with implicit definitions + // If we ever change our AST traversal to walk into class methods, + // this will be unnecessary. + } else if (getLangOptions().CPlusPlus && D.getDecl()) { + // Look for a declaration that's lexically in a record. + const FunctionDecl *FD = cast(D.getDecl()); + do { + if (isa(FD->getLexicalDeclContext())) { + if (FD->isImplicit()) { + assert(FD->isUsed() && "Sema didn't mark implicit function as used!"); + DeferredDeclsToEmit.push_back(D); + break; + } else if (FD->isThisDeclarationADefinition()) { + DeferredDeclsToEmit.push_back(D); + break; + } } - } + FD = FD->getPreviousDeclaration(); + } while (FD); } // Make sure the result is of the requested type. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 3506456eb7..17a1a1badb 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3735,6 +3735,12 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, NewFD->setAccess(AS_public); } + if (isa(NewFD) && DC == CurContext && IsFunctionDefinition) { + // A method is implicitly inline if it's defined in its class + // definition. + NewFD->setImplicitlyInline(); + } + if (SC == SC_Static && isa(NewFD) && !CurContext->isRecord()) { // C++ [class.static]p1: diff --git a/test/CodeGenCXX/inline-functions.cpp b/test/CodeGenCXX/inline-functions.cpp index 63a523a9de..69dfe0db98 100644 --- a/test/CodeGenCXX/inline-functions.cpp +++ b/test/CodeGenCXX/inline-functions.cpp @@ -29,3 +29,27 @@ inline void f1(int); void f1(int) { } void test_f1() { f1(17); } + +// PR8789 +namespace test1 { + template class ClassTemplate { + private: + friend void T::func(); + void g() {} + }; + + // CHECK: define linkonce_odr void @_ZN5test11C4funcEv( + + class C { + public: + void func() { + ClassTemplate ct; + ct.g(); + } + }; + + void f() { + C c; + c.func(); + } +} -- 2.40.0