diff options
| -rw-r--r-- | clang/lib/Sema/Sema.cpp | 33 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 22 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 13 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 59 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 2 | ||||
| -rw-r--r-- | clang/test/SemaCXX/virtual-member-functions-key-function.cpp | 22 | 
6 files changed, 118 insertions, 33 deletions
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 29efbd7becd..ef6147420be 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -728,18 +728,27 @@ void Sema::DeleteStmt(StmtTy *S) {  /// translation unit when EOF is reached and all but the top-level scope is  /// popped.  void Sema::ActOnEndOfTranslationUnit() { -  // C++: Perform implicit template instantiations. -  // -  // FIXME: When we perform these implicit instantiations, we do not carefully -  // keep track of the point of instantiation (C++ [temp.point]). This means -  // that name lookup that occurs within the template instantiation will -  // always happen at the end of the translation unit, so it will find -  // some names that should not be found. Although this is common behavior -  // for C++ compilers, it is technically wrong. In the future, we either need -  // to be able to filter the results of name lookup or we need to perform -  // template instantiations earlier. -  PerformPendingImplicitInstantiations(); - +   +  while (1) { +    // C++: Perform implicit template instantiations. +    // +    // FIXME: When we perform these implicit instantiations, we do not carefully +    // keep track of the point of instantiation (C++ [temp.point]). This means +    // that name lookup that occurs within the template instantiation will +    // always happen at the end of the translation unit, so it will find +    // some names that should not be found. Although this is common behavior +    // for C++ compilers, it is technically wrong. In the future, we either need +    // to be able to filter the results of name lookup or we need to perform +    // template instantiations earlier. +    PerformPendingImplicitInstantiations(); +     +    /// If ProcessPendingClassesWithUnmarkedVirtualMembers ends up marking  +    /// any virtual member functions it might lead to more pending template +    /// instantiations, which is why we need to loop here. +    if (!ProcessPendingClassesWithUnmarkedVirtualMembers()) +      break; +  } +      // Check for #pragma weak identifiers that were never declared    // FIXME: This will cause diagnostics to be emitted in a non-determinstic    // order!  Iterating over a densemap like this is bad. diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index e4ee8ce73cc..edb4528f7e6 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -32,6 +32,7 @@  #include "llvm/ADT/OwningPtr.h"  #include <deque>  #include <list> +#include <map>  #include <string>  #include <vector> @@ -2147,11 +2148,26 @@ public:    /// as referenced.    void MarkBaseAndMemberDestructorsReferenced(CXXDestructorDecl *Destructor); -  /// MaybeMarkVirtualImplicitMembersReferenced - If the passed in method is the +  /// ClassesWithUnmarkedVirtualMembers - Contains record decls whose virtual +  /// members might need to be marked as referenced. This is either done when +  /// the key function definition is emitted (this is handled by by  +  /// MaybeMarkVirtualMembersReferenced), or at the end of the translation unit +  /// (done by ProcessPendingClassesWithUnmarkedVirtualMembers). +  std::map<CXXRecordDecl *, SourceLocation> ClassesWithUnmarkedVirtualMembers; + +  /// MaybeMarkVirtualMembersReferenced - If the passed in method is the    /// key function of the record decl, will mark virtual member functions as     /// referenced. -  void MaybeMarkVirtualImplicitMembersReferenced(SourceLocation Loc,  -                                                 CXXMethodDecl *MD); +  void MaybeMarkVirtualMembersReferenced(SourceLocation Loc, CXXMethodDecl *MD); +   +  /// MarkVirtualMembersReferenced - Will mark all virtual members of the given +  /// CXXRecordDecl referenced. +  void MarkVirtualMembersReferenced(SourceLocation Loc, CXXRecordDecl *RD); + +  /// ProcessPendingClassesWithUnmarkedVirtualMembers - Will process classes  +  /// that might need to have their virtual members marked as referenced. +  /// Returns false if no work was done. +  bool ProcessPendingClassesWithUnmarkedVirtualMembers();    void AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a5314bb075e..bb3f8694250 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4114,16 +4114,9 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,      if (!FD->isInvalidDecl())        DiagnoseUnusedParameters(FD->param_begin(), FD->param_end()); -    if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) { -      // C++ [basic.def.odr]p2: -      //   [...] A virtual member function is used if it is not pure. [...] -      if (Method->isVirtual() && !Method->isPure()) -        MarkDeclarationReferenced(Method->getLocation(), Method); - -      if (!Method->isInlined()) -        MaybeMarkVirtualImplicitMembersReferenced(Method->getLocation(),  -                                                  Method); -    } +    if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) +      MaybeMarkVirtualMembersReferenced(Method->getLocation(), Method); +      assert(FD == getCurFunctionDecl() && "Function parsing confused");    } else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(dcl)) {      assert(MD == getCurMethodDecl() && "Method parsing confused"); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 1e96bf525ae..bd191ab8c96 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -3163,8 +3163,6 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,    } else {      Constructor->setUsed();    } - -  MaybeMarkVirtualImplicitMembersReferenced(CurrentLocation, Constructor);  }  void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation, @@ -5083,8 +5081,8 @@ Sema::ActOnCXXConditionDeclaration(Scope *S, Declarator &D) {    return Dcl;  } -void Sema::MaybeMarkVirtualImplicitMembersReferenced(SourceLocation Loc, -                                                     CXXMethodDecl *MD) { +void Sema::MaybeMarkVirtualMembersReferenced(SourceLocation Loc, +                                             CXXMethodDecl *MD) {    // Ignore dependent types.    if (MD->isDependentContext())      return; @@ -5095,6 +5093,16 @@ void Sema::MaybeMarkVirtualImplicitMembersReferenced(SourceLocation Loc,    if (!RD->isDynamicClass())      return; +  if (!MD->isOutOfLine()) { +    // The only inline functions we care about are constructors. We also defer +    // marking the virtual members as referenced until we've reached the end +    // of the translation unit. We do this because we need to know the key +    // function of the class in order to determine the key function. +    if (isa<CXXConstructorDecl>(MD)) +      ClassesWithUnmarkedVirtualMembers.insert(std::make_pair(RD, Loc)); +    return; +  } +      const CXXMethodDecl *KeyFunction = Context.getKeyFunction(RD);    if (!KeyFunction) { @@ -5107,10 +5115,45 @@ void Sema::MaybeMarkVirtualImplicitMembersReferenced(SourceLocation Loc,      return;    } -  if (CXXDestructorDecl *Dtor = RD->getDestructor(Context)) { -    if (Dtor->isImplicit() && Dtor->isVirtual()) -      MarkDeclarationReferenced(Loc, Dtor); +  // Mark the members as referenced. +  MarkVirtualMembersReferenced(Loc, RD); +  ClassesWithUnmarkedVirtualMembers.erase(RD); +} + +bool Sema::ProcessPendingClassesWithUnmarkedVirtualMembers() { +  if (ClassesWithUnmarkedVirtualMembers.empty()) +    return false; +   +  for (std::map<CXXRecordDecl *, SourceLocation>::iterator i =  +       ClassesWithUnmarkedVirtualMembers.begin(),  +       e = ClassesWithUnmarkedVirtualMembers.end(); i != e; ++i) { +    CXXRecordDecl *RD = i->first; +     +    const CXXMethodDecl *KeyFunction = Context.getKeyFunction(RD); +    if (KeyFunction) { +      // We know that the class has a key function. If the key function was +      // declared in this translation unit, then it the class decl would not  +      // have been in the ClassesWithUnmarkedVirtualMembers map. +      continue; +    } +     +    SourceLocation Loc = i->second; +    MarkVirtualMembersReferenced(Loc, RD);    } -  // FIXME: Need to handle the virtual assignment operator here too. +  ClassesWithUnmarkedVirtualMembers.clear(); +  return true;  } + +void Sema::MarkVirtualMembersReferenced(SourceLocation Loc, CXXRecordDecl *RD) { +  for (CXXRecordDecl::method_iterator i = RD->method_begin(),  +       e = RD->method_end(); i != e; ++i) { +    CXXMethodDecl *MD = *i; + +    // C++ [basic.def.odr]p2: +    //   [...] A virtual member function is used if it is not pure. [...] +    if (MD->isVirtual() && !MD->isPure()) +      MarkDeclarationReferenced(Loc, MD); +  } +} + diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 987de9b5b45..6c5a1ec05c5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6927,6 +6927,8 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {        if (!Constructor->isUsed())          DefineImplicitCopyConstructor(Loc, Constructor, TypeQuals);      } +     +    MaybeMarkVirtualMembersReferenced(Loc, Constructor);    } else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(D)) {      if (Destructor->isImplicit() && !Destructor->isUsed())        DefineImplicitDestructor(Loc, Destructor); diff --git a/clang/test/SemaCXX/virtual-member-functions-key-function.cpp b/clang/test/SemaCXX/virtual-member-functions-key-function.cpp new file mode 100644 index 00000000000..4e7ff69b2ed --- /dev/null +++ b/clang/test/SemaCXX/virtual-member-functions-key-function.cpp @@ -0,0 +1,22 @@ +// RUN: clang-cc -fsyntax-only -verify %s +struct A { +  virtual ~A(); +}; + +struct B : A {  // expected-error {{no suitable member 'operator delete' in 'B'}} +  B() { }  // expected-note {{implicit default destructor for 'struct B' first required here}} +  void operator delete(void *, int); // expected-note {{'operator delete' declared here}} +}; + +struct C : A {  // expected-error {{no suitable member 'operator delete' in 'C'}} +  void operator delete(void *, int); // expected-note {{'operator delete' declared here}} +}; + +void f() { +  // new B should mark the constructor as used, which then marks +  // all the virtual members as used, because B has no key function. +  (void)new B; + +  // Same here, except that C has an implicit constructor. +  (void)new C; // expected-note {{implicit default destructor for 'struct C' first required here}} +}  | 

