r336632 - [ODRHash] Merge the two function hashes into one.

Richard Trieu via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 9 18:40:50 PDT 2018


Author: rtrieu
Date: Mon Jul  9 18:40:50 2018
New Revision: 336632

URL: http://llvm.org/viewvc/llvm-project?rev=336632&view=rev
Log:
[ODRHash] Merge the two function hashes into one.

Functions that are a sub-Decl of a record were hashed differently than other
functions.  This change keeps the AddFunctionDecl function and the hash of
records now calls this function.  In addition, AddFunctionDecl has an option
to perform a hash as if the body was absent, which is required for some
checks after loading modules.  Additional logic prevents multiple error
message from being printed.

Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/ODRHash.h
    cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/ODRHash.cpp
    cfe/trunk/lib/Serialization/ASTReader.cpp
    cfe/trunk/test/Modules/odr_hash.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=336632&r1=336631&r2=336632&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Mon Jul  9 18:40:50 2018
@@ -2504,6 +2504,10 @@ public:
   /// stored on first call, then the stored value returned on the other calls.
   unsigned getODRHash();
 
+  /// Returns cached ODRHash of the function.  This must have been previously
+  /// computed and stored.
+  unsigned getODRHash() const;
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) {

Modified: cfe/trunk/include/clang/AST/ODRHash.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ODRHash.h?rev=336632&r1=336631&r2=336632&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ODRHash.h (original)
+++ cfe/trunk/include/clang/AST/ODRHash.h Mon Jul  9 18:40:50 2018
@@ -54,8 +54,9 @@ public:
   void AddCXXRecordDecl(const CXXRecordDecl *Record);
 
   // Use this for ODR checking functions between modules.  This method compares
-  // more information than the AddDecl class.
-  void AddFunctionDecl(const FunctionDecl *Function);
+  // more information than the AddDecl class.  SkipBody will process the
+  // hash as if the function has no body.
+  void AddFunctionDecl(const FunctionDecl *Function, bool SkipBody = false);
 
   // Process SubDecls of the main Decl.  This method calls the DeclVisitor
   // while AddDecl does not.

Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td?rev=336632&r1=336631&r2=336632&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Mon Jul  9 18:40:50 2018
@@ -192,6 +192,8 @@ def err_module_odr_violation_mismatch_de
   "%select{method %5|constructor|destructor}4 "
     "is %select{not deleted|deleted}6|"
   "%select{method %5|constructor|destructor}4 "
+    "is %select{not defaulted|defaulted}6|"
+  "%select{method %5|constructor|destructor}4 "
     "is %select{|pure }6%select{not virtual|virtual}7|"
   "%select{method %5|constructor|destructor}4 "
     "is %select{not static|static}6|"
@@ -217,6 +219,10 @@ def err_module_odr_violation_mismatch_de
     "with %6 template argument%s6|"
   "%select{method %5|constructor|destructor}4 "
     "with %6 for %ordinal7 template argument|"
+  "%select{method %5|constructor|destructor}4 "
+    "with %select{no body|body}6|"
+  "%select{method %5|constructor|destructor}4 "
+    "with body|"
   "%select{typedef|type alias}4 name %5|"
   "%select{typedef|type alias}4 %5 with underlying type %6|"
   "data member with name %4|"
@@ -257,6 +263,8 @@ def note_module_odr_violation_mismatch_d
   "%select{method %3|constructor|destructor}2 "
     "is %select{not deleted|deleted}4|"
   "%select{method %3|constructor|destructor}2 "
+    "is %select{not defaulted|defaulted}4|"
+  "%select{method %3|constructor|destructor}2 "
     "is %select{|pure }4%select{not virtual|virtual}5|"
   "%select{method %3|constructor|destructor}2 "
     "is %select{not static|static}4|"
@@ -282,6 +290,10 @@ def note_module_odr_violation_mismatch_d
     "with %4 template argument%s4|"
   "%select{method %3|constructor|destructor}2 "
     "with %4 for %ordinal5 template argument|"
+  "%select{method %3|constructor|destructor}2 "
+    "with %select{no body|body}4|"
+  "%select{method %3|constructor|destructor}2 "
+    "with different body|"
   "%select{typedef|type alias}2 name %3|"
   "%select{typedef|type alias}2 %3 with different underlying type %4|"
   "data member with name %2|"

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=336632&r1=336631&r2=336632&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Mon Jul  9 18:40:50 2018
@@ -3658,18 +3658,15 @@ unsigned FunctionDecl::getMemoryFunction
   return 0;
 }
 
+unsigned FunctionDecl::getODRHash() const {
+  assert(HasODRHash);
+  return ODRHash;
+}
+
 unsigned FunctionDecl::getODRHash() {
   if (HasODRHash)
     return ODRHash;
 
-  if (FunctionDecl *Definition = getDefinition()) {
-    if (Definition != this) {
-      HasODRHash = true;
-      ODRHash = Definition->getODRHash();
-      return ODRHash;
-    }
-  }
-
   if (auto *FT = getInstantiatedFromMemberFunction()) {
     HasODRHash = true;
     ODRHash = FT->getODRHash();

Modified: cfe/trunk/lib/AST/ODRHash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ODRHash.cpp?rev=336632&r1=336631&r2=336632&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ODRHash.cpp (original)
+++ cfe/trunk/lib/AST/ODRHash.cpp Mon Jul  9 18:40:50 2018
@@ -317,35 +317,14 @@ public:
   }
 
   void VisitFunctionDecl(const FunctionDecl *D) {
-    ID.AddInteger(D->getStorageClass());
-    Hash.AddBoolean(D->isInlineSpecified());
-    Hash.AddBoolean(D->isVirtualAsWritten());
-    Hash.AddBoolean(D->isPure());
-    Hash.AddBoolean(D->isDeletedAsWritten());
-
-    ID.AddInteger(D->param_size());
-
-    for (auto *Param : D->parameters()) {
-      Hash.AddSubDecl(Param);
-    }
-
-    AddQualType(D->getReturnType());
-
-    const auto* SpecializationArgs = D->getTemplateSpecializationArgs();
-    Hash.AddBoolean(SpecializationArgs);
-    if (SpecializationArgs) {
-      ID.AddInteger(SpecializationArgs->size());
-      for (const TemplateArgument &TA : SpecializationArgs->asArray()) {
-        Hash.AddTemplateArgument(TA);
-      }
-    }
+    // Handled by the ODRHash for FunctionDecl
+    ID.AddInteger(D->getODRHash());
 
     Inherited::VisitFunctionDecl(D);
   }
 
   void VisitCXXMethodDecl(const CXXMethodDecl *D) {
-    Hash.AddBoolean(D->isConst());
-    Hash.AddBoolean(D->isVolatile());
+    // Handled by the ODRHash for FunctionDecl
 
     Inherited::VisitCXXMethodDecl(D);
   }
@@ -425,7 +404,6 @@ public:
   }
 
   void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
-    Visit(D->getTemplatedDecl());
     AddDecl(D->getTemplatedDecl());
     Inherited::VisitFunctionTemplateDecl(D);
   }
@@ -479,9 +457,13 @@ void ODRHash::AddCXXRecordDecl(const CXX
   // Filter out sub-Decls which will not be processed in order to get an
   // accurate count of Decl's.
   llvm::SmallVector<const Decl *, 16> Decls;
-  for (const Decl *SubDecl : Record->decls()) {
+  for (Decl *SubDecl : Record->decls()) {
     if (isWhitelistedDecl(SubDecl, Record)) {
       Decls.push_back(SubDecl);
+      if (auto *Function = dyn_cast<FunctionDecl>(SubDecl)) {
+        // Compute/Preload ODRHash into FunctionDecl.
+        Function->getODRHash();
+      }
     }
   }
 
@@ -505,25 +487,48 @@ void ODRHash::AddCXXRecordDecl(const CXX
   }
 }
 
-void ODRHash::AddFunctionDecl(const FunctionDecl *Function) {
+void ODRHash::AddFunctionDecl(const FunctionDecl *Function,
+                              bool SkipBody) {
   assert(Function && "Expecting non-null pointer.");
 
-  // Skip hashing these kinds of function.
-  if (Function->isImplicit()) return;
-  if (Function->isDefaulted()) return;
-  if (Function->isDeleted()) return;
-  if (!Function->hasBody()) return;
-  if (!Function->getBody()) return;
-
   // Skip functions that are specializations or in specialization context.
   const DeclContext *DC = Function;
   while (DC) {
     if (isa<ClassTemplateSpecializationDecl>(DC)) return;
-    if (auto *F = dyn_cast<FunctionDecl>(DC))
-      if (F->isFunctionTemplateSpecialization()) return;
+    if (auto *F = dyn_cast<FunctionDecl>(DC)) {
+      if (F->isFunctionTemplateSpecialization()) {
+        if (!isa<CXXMethodDecl>(DC)) return;
+        if (DC->getLexicalParent()->isFileContext()) return;
+        // Inline method specializations are the only supported
+        // specialization for now.
+      }
+    }
     DC = DC->getParent();
   }
 
+  ID.AddInteger(Function->getDeclKind());
+
+  const auto *SpecializationArgs = Function->getTemplateSpecializationArgs();
+  AddBoolean(SpecializationArgs);
+  if (SpecializationArgs) {
+    ID.AddInteger(SpecializationArgs->size());
+    for (const TemplateArgument &TA : SpecializationArgs->asArray()) {
+      AddTemplateArgument(TA);
+    }
+  }
+
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(Function)) {
+    AddBoolean(Method->isConst());
+    AddBoolean(Method->isVolatile());
+  }
+
+  ID.AddInteger(Function->getStorageClass());
+  AddBoolean(Function->isInlineSpecified());
+  AddBoolean(Function->isVirtualAsWritten());
+  AddBoolean(Function->isPure());
+  AddBoolean(Function->isDeletedAsWritten());
+  AddBoolean(Function->isExplicitlyDefaulted());
+
   AddDecl(Function);
 
   AddQualType(Function->getReturnType());
@@ -532,7 +537,21 @@ void ODRHash::AddFunctionDecl(const Func
   for (auto Param : Function->parameters())
     AddSubDecl(Param);
 
-  AddStmt(Function->getBody());
+  if (SkipBody) {
+    AddBoolean(false);
+    return;
+  }
+
+  const bool HasBody = Function->isThisDeclarationADefinition() &&
+                       !Function->isDefaulted() && !Function->isDeleted() &&
+                       !Function->isLateTemplateParsed();
+  AddBoolean(HasBody);
+  if (HasBody) {
+    auto *Body = Function->getBody();
+    AddBoolean(Body);
+    if (Body)
+      AddStmt(Body);
+  }
 }
 
 void ODRHash::AddDecl(const Decl *D) {

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=336632&r1=336631&r2=336632&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Mon Jul  9 18:40:50 2018
@@ -9397,7 +9397,15 @@ void ASTReader::finishPendingActions() {
         if (!FD->isLateTemplateParsed() &&
             !NonConstDefn->isLateTemplateParsed() &&
             FD->getODRHash() != NonConstDefn->getODRHash()) {
-          PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
+          if (!isa<CXXMethodDecl>(FD)) {
+            PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
+          } else if (FD->getLexicalParent()->isFileContext() &&
+                     NonConstDefn->getLexicalParent()->isFileContext()) {
+            // Only diagnose out-of-line method definitions.  If they are
+            // in class definitions, then an error will be generated when
+            // processing the class bodies.
+            PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
+          }
         }
       }
       continue;
@@ -10069,6 +10077,7 @@ void ASTReader::diagnoseOdrViolations()
         FieldDifferentInitializers,
         MethodName,
         MethodDeleted,
+        MethodDefaulted,
         MethodVirtual,
         MethodStatic,
         MethodVolatile,
@@ -10082,6 +10091,8 @@ void ASTReader::diagnoseOdrViolations()
         MethodNoTemplateArguments,
         MethodDifferentNumberTemplateArguments,
         MethodDifferentTemplateArgument,
+        MethodSingleBody,
+        MethodDifferentBody,
         TypedefName,
         TypedefType,
         VarName,
@@ -10315,8 +10326,8 @@ void ASTReader::diagnoseOdrViolations()
           break;
         }
 
-        const bool FirstDeleted = FirstMethod->isDeleted();
-        const bool SecondDeleted = SecondMethod->isDeleted();
+        const bool FirstDeleted = FirstMethod->isDeletedAsWritten();
+        const bool SecondDeleted = SecondMethod->isDeletedAsWritten();
         if (FirstDeleted != SecondDeleted) {
           ODRDiagError(FirstMethod->getLocation(),
                        FirstMethod->getSourceRange(), MethodDeleted)
@@ -10329,6 +10340,20 @@ void ASTReader::diagnoseOdrViolations()
           break;
         }
 
+        const bool FirstDefaulted = FirstMethod->isExplicitlyDefaulted();
+        const bool SecondDefaulted = SecondMethod->isExplicitlyDefaulted();
+        if (FirstDefaulted != SecondDefaulted) {
+          ODRDiagError(FirstMethod->getLocation(),
+                       FirstMethod->getSourceRange(), MethodDefaulted)
+              << FirstMethodType << FirstName << FirstDefaulted;
+
+          ODRDiagNote(SecondMethod->getLocation(),
+                      SecondMethod->getSourceRange(), MethodDefaulted)
+              << SecondMethodType << SecondName << SecondDefaulted;
+          Diagnosed = true;
+          break;
+        }
+
         const bool FirstVirtual = FirstMethod->isVirtualAsWritten();
         const bool SecondVirtual = SecondMethod->isVirtualAsWritten();
         const bool FirstPure = FirstMethod->isPure();
@@ -10594,6 +10619,44 @@ void ASTReader::diagnoseOdrViolations()
             break;
           }
         }
+
+        // Compute the hash of the method as if it has no body.
+        auto ComputeCXXMethodODRHash = [&Hash](const CXXMethodDecl *D) {
+          Hash.clear();
+          Hash.AddFunctionDecl(D, true /*SkipBody*/);
+          return Hash.CalculateHash();
+        };
+
+        // Compare the hash generated to the hash stored.  A difference means
+        // that a body was present in the original source.  Due to merging,
+        // the stardard way of detecting a body will not work.
+        const bool HasFirstBody =
+            ComputeCXXMethodODRHash(FirstMethod) != FirstMethod->getODRHash();
+        const bool HasSecondBody =
+            ComputeCXXMethodODRHash(SecondMethod) != SecondMethod->getODRHash();
+
+        if (HasFirstBody != HasSecondBody) {
+          ODRDiagError(FirstMethod->getLocation(),
+                       FirstMethod->getSourceRange(), MethodSingleBody)
+              << FirstMethodType << FirstName << HasFirstBody;
+          ODRDiagNote(SecondMethod->getLocation(),
+                      SecondMethod->getSourceRange(), MethodSingleBody)
+              << SecondMethodType << SecondName << HasSecondBody;
+          Diagnosed = true;
+          break;
+        }
+
+        if (HasFirstBody && HasSecondBody) {
+          ODRDiagError(FirstMethod->getLocation(),
+                       FirstMethod->getSourceRange(), MethodDifferentBody)
+              << FirstMethodType << FirstName;
+          ODRDiagNote(SecondMethod->getLocation(),
+                      SecondMethod->getSourceRange(), MethodDifferentBody)
+              << SecondMethodType << SecondName;
+          Diagnosed = true;
+          break;
+        }
+
         break;
       }
       case TypeAlias:

Modified: cfe/trunk/test/Modules/odr_hash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/odr_hash.cpp?rev=336632&r1=336631&r2=336632&view=diff
==============================================================================
--- cfe/trunk/test/Modules/odr_hash.cpp (original)
+++ cfe/trunk/test/Modules/odr_hash.cpp Mon Jul  9 18:40:50 2018
@@ -660,6 +660,202 @@ Invalid1* i1;
 #undef DECLS
 }  // namespace Method
 
+namespace MethodBody {
+#if defined(FIRST)
+struct S1 {
+  int A() { return 0; }
+};
+#elif defined(SECOND)
+struct S1 {
+  int A() { return 0; }
+};
+#else
+S1 s1;
+#endif
+
+#if defined(FIRST)
+struct S2 {
+  int BothBodies() { return 0; }
+};
+#elif defined(SECOND)
+struct S2 {
+  int BothBodies() { return 1; }
+};
+#else
+S2 s2;
+// expected-error at first.h:* {{'MethodBody::S2' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'BothBodies' with body}}
+// expected-note at second.h:* {{but in 'SecondModule' found method 'BothBodies' with different body}}
+#endif
+
+#if defined(FIRST)
+struct S3 {
+  int FirstBody() { return 0; }
+};
+#elif defined(SECOND)
+struct S3 {
+  int FirstBody();
+};
+#else
+S3 s3;
+// expected-error at first.h:* {{'MethodBody::S3' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'FirstBody' with body}}
+// expected-note at second.h:* {{but in 'SecondModule' found method 'FirstBody' with no body}}
+#endif
+
+#if defined(FIRST)
+struct S4 {
+  int SecondBody();
+};
+#elif defined(SECOND)
+struct S4 {
+  int SecondBody() { return 0; }
+};
+#else
+S4 s4;
+// expected-error at first.h:* {{'MethodBody::S4' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'SecondBody' with no body}}
+// expected-note at second.h:* {{but in 'SecondModule' found method 'SecondBody' with body}}
+#endif
+
+#if defined(FIRST)
+struct S5 {
+  int FirstBodySecondOutOfLine() { return 0; }
+};
+#elif defined(SECOND)
+struct S5 {
+  int FirstBodySecondOutOfLine();
+};
+int S5::FirstBodySecondOutOfLine() { return 0; }
+#else
+S5 s5;
+// expected-error at second.h:* {{'MethodBody::S5' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'FirstBodySecondOutOfLine' with no body}}
+// expected-note at first.h:* {{but in 'FirstModule' found method 'FirstBodySecondOutOfLine' with body}}
+#endif
+
+#if defined(FIRST)
+struct S6 {
+  int FirstOutOfLineSecondBody();
+};
+int S6::FirstOutOfLineSecondBody() { return 0; }
+#elif defined(SECOND)
+struct S6 {
+  int FirstOutOfLineSecondBody() { return 0; }
+};
+#else
+S6 s6;
+// expected-error at first.h:* {{'MethodBody::S6' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'FirstOutOfLineSecondBody' with no body}}
+// expected-note at second.h:* {{but in 'SecondModule' found method 'FirstOutOfLineSecondBody' with body}}
+#endif
+
+#if defined(FIRST)
+struct S7 {
+  int BothOutOfLine();
+};
+int S7::BothOutOfLine() { return 1; }
+#elif defined(SECOND)
+struct S7 {
+  int BothOutOfLine();
+};
+int S7::BothOutOfLine() { return 0; }
+#else
+S7 s7;
+// expected-error at second.h:* {{'MethodBody::S7::BothOutOfLine' has different definitions in different modules; definition in module 'SecondModule' first difference is function body}}
+// expected-note at first.h:* {{but in 'FirstModule' found a different body}}
+#endif
+
+#if defined(FIRST)
+struct S8 {
+  int FirstBodySecondOutOfLine() { return 0; }
+};
+#elif defined(SECOND)
+struct S8 {
+  int FirstBodySecondOutOfLine();
+};
+int S8::FirstBodySecondOutOfLine() { return 1; }
+#else
+S8 s8;
+// expected-error at second.h:* {{'MethodBody::S8' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'FirstBodySecondOutOfLine' with no body}}
+// expected-note at first.h:* {{but in 'FirstModule' found method 'FirstBodySecondOutOfLine' with body}}
+#endif
+
+#if defined(FIRST)
+struct S9 {
+  int FirstOutOfLineSecondBody();
+};
+int S9::FirstOutOfLineSecondBody() { return 1; }
+#elif defined(SECOND)
+struct S9 {
+  int FirstOutOfLineSecondBody() { return 0; }
+};
+#else
+S9 s9;
+// expected-error at first.h:* {{'MethodBody::S9' has different definitions in different modules; first difference is definition in module 'FirstModule' found method 'FirstOutOfLineSecondBody' with no body}}
+// expected-note at second.h:* {{but in 'SecondModule' found method 'FirstOutOfLineSecondBody' with body}}
+#endif
+
+#if defined(FIRST)
+struct S10 {
+  S10(int);
+  S10() = delete;
+};
+#elif defined(SECOND)
+struct S10 {
+  S10(int);
+  S10();
+};
+#else
+S10 s10(10);
+// expected-error at first.h:* {{'MethodBody::S10' has different definitions in different modules; first difference is definition in module 'FirstModule' found constructor is deleted}}
+// expected-note at second.h:* {{but in 'SecondModule' found constructor is not deleted}}
+#endif
+
+#if defined(FIRST)
+struct S11 {
+  S11() = default;
+};
+#elif defined(SECOND)
+struct S11 {
+  S11();
+};
+#else
+S11 s11;
+// expected-error at first.h:* {{'MethodBody::S11' has different definitions in different modules; first difference is definition in module 'FirstModule' found constructor is defaulted}}
+// expected-note at second.h:* {{but in 'SecondModule' found constructor is not defaulted}}
+#endif
+
+#define DECLS(CLASSNAME) \
+  CLASSNAME() = default; \
+  ~CLASSNAME() = delete; \
+  void A();              \
+  void B() { return; };  \
+  void C();              \
+  void D();
+
+#define OUTOFLINEDEFS(CLASSNAME) \
+  void CLASSNAME::C() {}         \
+  void CLASSNAME::D() { return; }
+
+#if defined(FIRST) || defined(SECOND)
+struct Valid1 {
+  DECLS(Valid1)
+};
+OUTOFLINEDEFS(Valid1)
+#else
+Valid1* v1;
+#endif
+
+#if defined(FIRST) || defined(SECOND)
+struct Invalid1 {
+  DECLS(Invalid1)
+  ACCESS
+};
+OUTOFLINEDEFS(Invalid1)
+#else
+Invalid1* i1;
+// expected-error at first.h:* {{'MethodBody::Invalid1' has different definitions in different modules; first difference is definition in module 'FirstModule' found public access specifier}}
+// expected-note at second.h:* {{but in 'SecondModule' found private access specifier}}
+#endif
+#undef DECLS
+}  // namespace MethodBody
+
 namespace Constructor {
 #if defined(FIRST)
 struct S1 {
@@ -3631,6 +3827,32 @@ template <int> void S11::foo() {}
 S11 s11;
 #endif
 
+#if defined(FIRST)
+struct S12 {
+  void foo(int x);
+};
+#elif defined(SECOND)
+struct S12 {
+  void foo(int x);
+};
+void S12::foo(int y) {}
+#else
+S12 s12;
+#endif
+
+#if defined(FIRST)
+struct S13 {
+  void foo(int x);
+};
+void S13::foo(int y) {}
+#elif defined(SECOND)
+struct S13 {
+  void foo(int x);
+};
+void S13::foo(int y) {}
+#else
+S13 s13;
+#endif
 }  // namespace FunctionDecl
 
 namespace DeclTemplateArguments {




More information about the cfe-commits mailing list