r295890 - [ODRHash] static_cast and Stmt hashing.

Richard Trieu via cfe-commits cfe-commits at lists.llvm.org
Wed Feb 22 14:22:42 PST 2017


Author: rtrieu
Date: Wed Feb 22 16:22:42 2017
New Revision: 295890

URL: http://llvm.org/viewvc/llvm-project?rev=295890&view=rev
Log:
[ODRHash] static_cast and Stmt hashing.

Add support for static_cast in classes.  Add pointer-independent profiling for
Stmt's, sharing most of the logic with Stmt::Profile.  This is the first of the
deep sub-Decl diffing for error messages.

Differential Revision: https://reviews.llvm.org/D21675

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

Modified: cfe/trunk/include/clang/AST/Stmt.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Stmt.h?rev=295890&r1=295889&r2=295890&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Stmt.h (original)
+++ cfe/trunk/include/clang/AST/Stmt.h Wed Feb 22 16:22:42 2017
@@ -39,6 +39,7 @@ namespace clang {
   class Expr;
   class IdentifierInfo;
   class LabelDecl;
+  class ODRHash;
   class ParmVarDecl;
   class PrinterHelper;
   struct PrintingPolicy;
@@ -436,6 +437,15 @@ public:
   /// written in the source.
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                bool Canonical) const;
+
+  /// \brief Calculate a unique representation for a statement that is
+  /// stable across compiler invocations.
+  ///
+  /// \param ID profile information will be stored in ID.
+  ///
+  /// \param Hash an ODRHash object which will be called where pointers would
+  /// have been used in the Profile function.
+  void ProcessODRHash(llvm::FoldingSetNodeID &ID, ODRHash& Hash) const;
 };
 
 /// DeclStmt - Adaptor class for mixing declarations with statements and

Modified: cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td?rev=295890&r1=295889&r2=295890&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSerializationKinds.td Wed Feb 22 16:22:42 2017
@@ -121,10 +121,24 @@ def err_module_odr_violation_mismatch_de
   "%q0 has different definitions in different modules; first difference is "
   "%select{definition in module '%2'|defined here}1 found "
   "%select{end of class|public access specifier|private access specifier|"
-  "protected access specifier}3">;
+  "protected access specifier|static assert}3">;
 def note_module_odr_violation_mismatch_decl : Note<"but in '%0' found "
   "%select{end of class|public access specifier|private access specifier|"
-  "protected access specifier}1">;
+  "protected access specifier|static assert}1">;
+
+def err_module_odr_violation_mismatch_decl_diff : Error<
+  "%q0 has different definitions in different modules; first difference is "
+  "%select{definition in module '%2'|defined here}1 found "
+  "%select{"
+  "static assert with condition|"
+  "static assert with message|"
+  "static assert with %select{|no }4message}3">;
+
+def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
+  "%select{"
+  "static assert with different condition|"
+  "static assert with different message|"
+  "static assert with %select{|no }2message}1">;
 
 def warn_module_uses_date_time : Warning<
   "%select{precompiled header|module}0 uses __DATE__ or __TIME__">,

Modified: cfe/trunk/lib/AST/ODRHash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ODRHash.cpp?rev=295890&r1=295889&r2=295890&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ODRHash.cpp (original)
+++ cfe/trunk/lib/AST/ODRHash.cpp Wed Feb 22 16:22:42 2017
@@ -22,7 +22,10 @@
 
 using namespace clang;
 
-void ODRHash::AddStmt(const Stmt *S) {}
+void ODRHash::AddStmt(const Stmt *S) {
+  assert(S && "Expecting non-null pointer.");
+  S->ProcessODRHash(ID, *this);
+}
 void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {}
 void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {}
 void ODRHash::AddTemplateName(TemplateName Name) {}
@@ -74,10 +77,18 @@ unsigned ODRHash::CalculateHash() {
 class ODRDeclVisitor : public ConstDeclVisitor<ODRDeclVisitor> {
   typedef ConstDeclVisitor<ODRDeclVisitor> Inherited;
   llvm::FoldingSetNodeID &ID;
+  ODRHash &Hash;
 
 public:
-  ODRDeclVisitor(llvm::FoldingSetNodeID &ID)
-      : ID(ID) {}
+  ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
+      : ID(ID), Hash(Hash) {}
+
+  void AddStmt(const Stmt *S) {
+    Hash.AddBoolean(S);
+    if (S) {
+      Hash.AddStmt(S);
+    }
+  }
 
   void Visit(const Decl *D) {
     ID.AddInteger(D->getKind());
@@ -88,6 +99,13 @@ public:
     ID.AddInteger(D->getAccess());
     Inherited::VisitAccessSpecDecl(D);
   }
+
+  void VisitStaticAssertDecl(const StaticAssertDecl *D) {
+    AddStmt(D->getAssertExpr());
+    AddStmt(D->getMessage());
+
+    Inherited::VisitStaticAssertDecl(D);
+  }
 };
 
 // Only allow a small portion of Decl's to be processed.  Remove this once
@@ -100,6 +118,7 @@ bool ODRHash::isWhitelistedDecl(const De
     default:
       return false;
     case Decl::AccessSpec:
+    case Decl::StaticAssert:
       return true;
   }
 }
@@ -108,7 +127,7 @@ void ODRHash::AddSubDecl(const Decl *D)
   assert(D && "Expecting non-null pointer.");
   AddDecl(D);
 
-  ODRDeclVisitor(ID).Visit(D);
+  ODRDeclVisitor(ID, *this).Visit(D);
 }
 
 void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {

Modified: cfe/trunk/lib/AST/StmtProfile.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/StmtProfile.cpp?rev=295890&r1=295889&r2=295890&view=diff
==============================================================================
--- cfe/trunk/lib/AST/StmtProfile.cpp (original)
+++ cfe/trunk/lib/AST/StmtProfile.cpp Wed Feb 22 16:22:42 2017
@@ -19,20 +19,22 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ExprOpenMP.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/StmtVisitor.h"
 #include "llvm/ADT/FoldingSet.h"
 using namespace clang;
 
 namespace {
   class StmtProfiler : public ConstStmtVisitor<StmtProfiler> {
+  protected:
     llvm::FoldingSetNodeID &ID;
-    const ASTContext &Context;
     bool Canonical;
 
   public:
-    StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
-                 bool Canonical)
-      : ID(ID), Context(Context), Canonical(Canonical) { }
+    StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical)
+        : ID(ID), Canonical(Canonical) {}
+
+    virtual ~StmtProfiler() {}
 
     void VisitStmt(const Stmt *S);
 
@@ -41,22 +43,25 @@ namespace {
 
     /// \brief Visit a declaration that is referenced within an expression
     /// or statement.
-    void VisitDecl(const Decl *D);
+    virtual void VisitDecl(const Decl *D) = 0;
 
     /// \brief Visit a type that is referenced within an expression or
     /// statement.
-    void VisitType(QualType T);
+    virtual void VisitType(QualType T) = 0;
 
     /// \brief Visit a name that occurs within an expression or statement.
-    void VisitName(DeclarationName Name);
+    virtual void VisitName(DeclarationName Name) = 0;
+
+    /// \brief Visit identifiers that are not in Decl's or Type's.
+    virtual void VisitIdentifierInfo(IdentifierInfo *II) = 0;
 
     /// \brief Visit a nested-name-specifier that occurs within an expression
     /// or statement.
-    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS);
+    virtual void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) = 0;
 
     /// \brief Visit a template name that occurs within an expression or
     /// statement.
-    void VisitTemplateName(TemplateName Name);
+    virtual void VisitTemplateName(TemplateName Name) = 0;
 
     /// \brief Visit template arguments that occur within an expression or
     /// statement.
@@ -66,6 +71,127 @@ namespace {
     /// \brief Visit a single template argument.
     void VisitTemplateArgument(const TemplateArgument &Arg);
   };
+
+  class StmtProfilerWithPointers : public StmtProfiler {
+    const ASTContext &Context;
+
+  public:
+    StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID,
+                             const ASTContext &Context, bool Canonical)
+        : StmtProfiler(ID, Canonical), Context(Context) {}
+  private:
+    void VisitDecl(const Decl *D) override {
+      ID.AddInteger(D ? D->getKind() : 0);
+
+      if (Canonical && D) {
+        if (const NonTypeTemplateParmDecl *NTTP =
+                dyn_cast<NonTypeTemplateParmDecl>(D)) {
+          ID.AddInteger(NTTP->getDepth());
+          ID.AddInteger(NTTP->getIndex());
+          ID.AddBoolean(NTTP->isParameterPack());
+          VisitType(NTTP->getType());
+          return;
+        }
+
+        if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
+          // The Itanium C++ ABI uses the type, scope depth, and scope
+          // index of a parameter when mangling expressions that involve
+          // function parameters, so we will use the parameter's type for
+          // establishing function parameter identity. That way, our
+          // definition of "equivalent" (per C++ [temp.over.link]) is at
+          // least as strong as the definition of "equivalent" used for
+          // name mangling.
+          VisitType(Parm->getType());
+          ID.AddInteger(Parm->getFunctionScopeDepth());
+          ID.AddInteger(Parm->getFunctionScopeIndex());
+          return;
+        }
+
+        if (const TemplateTypeParmDecl *TTP =
+                dyn_cast<TemplateTypeParmDecl>(D)) {
+          ID.AddInteger(TTP->getDepth());
+          ID.AddInteger(TTP->getIndex());
+          ID.AddBoolean(TTP->isParameterPack());
+          return;
+        }
+
+        if (const TemplateTemplateParmDecl *TTP =
+                dyn_cast<TemplateTemplateParmDecl>(D)) {
+          ID.AddInteger(TTP->getDepth());
+          ID.AddInteger(TTP->getIndex());
+          ID.AddBoolean(TTP->isParameterPack());
+          return;
+        }
+      }
+
+      ID.AddPointer(D ? D->getCanonicalDecl() : nullptr);
+    }
+
+    void VisitType(QualType T) override {
+      if (Canonical)
+        T = Context.getCanonicalType(T);
+
+      ID.AddPointer(T.getAsOpaquePtr());
+    }
+
+    void VisitName(DeclarationName Name) override {
+      ID.AddPointer(Name.getAsOpaquePtr());
+    }
+
+    void VisitIdentifierInfo(IdentifierInfo *II) override {
+      ID.AddPointer(II);
+    }
+
+    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
+      if (Canonical)
+        NNS = Context.getCanonicalNestedNameSpecifier(NNS);
+      ID.AddPointer(NNS);
+    }
+
+    void VisitTemplateName(TemplateName Name) override {
+      if (Canonical)
+        Name = Context.getCanonicalTemplateName(Name);
+
+      Name.Profile(ID);
+    }
+  };
+
+  class StmtProfilerWithoutPointers : public StmtProfiler {
+    ODRHash &Hash;
+  public:
+    StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
+        : StmtProfiler(ID, false), Hash(Hash) {}
+
+  private:
+    void VisitType(QualType T) override {
+      Hash.AddQualType(T);
+    }
+
+    void VisitName(DeclarationName Name) override {
+      Hash.AddDeclarationName(Name);
+    }
+    void VisitIdentifierInfo(IdentifierInfo *II) override {
+      ID.AddBoolean(II);
+      if (II) {
+        Hash.AddIdentifierInfo(II);
+      }
+    }
+    void VisitDecl(const Decl *D) override {
+      ID.AddBoolean(D);
+      if (D) {
+        Hash.AddDecl(D);
+      }
+    }
+    void VisitTemplateName(TemplateName Name) override {
+      Hash.AddTemplateName(Name);
+    }
+    void VisitNestedNameSpecifier(NestedNameSpecifier *NNS) override {
+      ID.AddBoolean(NNS);
+      if (NNS) {
+        Hash.AddNestedNameSpecifier(NNS);
+      }
+    }
+  };
 }
 
 void StmtProfiler::VisitStmt(const Stmt *S) {
@@ -853,7 +979,7 @@ void StmtProfiler::VisitOffsetOfExpr(con
       break;
 
     case OffsetOfNode::Identifier:
-      ID.AddPointer(ON.getFieldName());
+      VisitIdentifierInfo(ON.getFieldName());
       break;
 
     case OffsetOfNode::Base:
@@ -861,7 +987,7 @@ void StmtProfiler::VisitOffsetOfExpr(con
       break;
     }
   }
-  
+
   VisitExpr(S);
 }
 
@@ -1451,7 +1577,7 @@ StmtProfiler::VisitCXXPseudoDestructorEx
   if (S->getDestroyedTypeInfo())
     VisitType(S->getDestroyedType());
   else
-    ID.AddPointer(S->getDestroyedTypeIdentifier());
+    VisitIdentifierInfo(S->getDestroyedTypeIdentifier());
 }
 
 void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {
@@ -1701,77 +1827,6 @@ void StmtProfiler::VisitObjCAvailability
   VisitExpr(S);
 }
 
-void StmtProfiler::VisitDecl(const Decl *D) {
-  ID.AddInteger(D? D->getKind() : 0);
-
-  if (Canonical && D) {
-    if (const NonTypeTemplateParmDecl *NTTP =
-          dyn_cast<NonTypeTemplateParmDecl>(D)) {
-      ID.AddInteger(NTTP->getDepth());
-      ID.AddInteger(NTTP->getIndex());
-      ID.AddBoolean(NTTP->isParameterPack());
-      VisitType(NTTP->getType());
-      return;
-    }
-
-    if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) {
-      // The Itanium C++ ABI uses the type, scope depth, and scope
-      // index of a parameter when mangling expressions that involve
-      // function parameters, so we will use the parameter's type for
-      // establishing function parameter identity. That way, our
-      // definition of "equivalent" (per C++ [temp.over.link]) is at
-      // least as strong as the definition of "equivalent" used for
-      // name mangling.
-      VisitType(Parm->getType());
-      ID.AddInteger(Parm->getFunctionScopeDepth());
-      ID.AddInteger(Parm->getFunctionScopeIndex());
-      return;
-    }
-
-    if (const TemplateTypeParmDecl *TTP =
-          dyn_cast<TemplateTypeParmDecl>(D)) {
-      ID.AddInteger(TTP->getDepth());
-      ID.AddInteger(TTP->getIndex());
-      ID.AddBoolean(TTP->isParameterPack());
-      return;
-    }
-
-    if (const TemplateTemplateParmDecl *TTP =
-          dyn_cast<TemplateTemplateParmDecl>(D)) {
-      ID.AddInteger(TTP->getDepth());
-      ID.AddInteger(TTP->getIndex());
-      ID.AddBoolean(TTP->isParameterPack());
-      return;
-    }
-  }
-
-  ID.AddPointer(D? D->getCanonicalDecl() : nullptr);
-}
-
-void StmtProfiler::VisitType(QualType T) {
-  if (Canonical)
-    T = Context.getCanonicalType(T);
-
-  ID.AddPointer(T.getAsOpaquePtr());
-}
-
-void StmtProfiler::VisitName(DeclarationName Name) {
-  ID.AddPointer(Name.getAsOpaquePtr());
-}
-
-void StmtProfiler::VisitNestedNameSpecifier(NestedNameSpecifier *NNS) {
-  if (Canonical)
-    NNS = Context.getCanonicalNestedNameSpecifier(NNS);
-  ID.AddPointer(NNS);
-}
-
-void StmtProfiler::VisitTemplateName(TemplateName Name) {
-  if (Canonical)
-    Name = Context.getCanonicalTemplateName(Name);
-
-  Name.Profile(ID);
-}
-
 void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc *Args,
                                           unsigned NumArgs) {
   ID.AddInteger(NumArgs);
@@ -1821,6 +1876,12 @@ void StmtProfiler::VisitTemplateArgument
 
 void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                    bool Canonical) const {
-  StmtProfiler Profiler(ID, Context, Canonical);
+  StmtProfilerWithPointers Profiler(ID, Context, Canonical);
+  Profiler.Visit(this);
+}
+
+void Stmt::ProcessODRHash(llvm::FoldingSetNodeID &ID,
+                          class ODRHash &Hash) const {
+  StmtProfilerWithoutPointers Profiler(ID, Hash);
   Profiler.Visit(this);
 }

Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=295890&r1=295889&r2=295890&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Wed Feb 22 16:22:42 2017
@@ -8955,6 +8955,7 @@ void ASTReader::diagnoseOdrViolations()
         PublicSpecifer,
         PrivateSpecifer,
         ProtectedSpecifer,
+        StaticAssert,
         Other
       } FirstDiffType = Other,
         SecondDiffType = Other;
@@ -8976,6 +8977,8 @@ void ASTReader::diagnoseOdrViolations()
             break;
           }
           llvm_unreachable("Invalid access specifier");
+        case Decl::StaticAssert:
+          return StaticAssert;
         }
       };
 
@@ -9047,6 +9050,104 @@ void ASTReader::diagnoseOdrViolations()
         break;
       }
 
+      assert(FirstDiffType == SecondDiffType);
+
+      // Used with err_module_odr_violation_mismatch_decl_diff and
+      // note_module_odr_violation_mismatch_decl_diff
+      enum ODRDeclDifference{
+        StaticAssertCondition,
+        StaticAssertMessage,
+        StaticAssertOnlyMessage,
+      };
+
+      // These lambdas have the common portions of the ODR diagnostics.  This
+      // has the same return as Diag(), so addition parameters can be passed
+      // in with operator<<
+      auto ODRDiagError = [FirstRecord, &FirstModule, this](
+          SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {
+        return Diag(Loc, diag::err_module_odr_violation_mismatch_decl_diff)
+               << FirstRecord << FirstModule.empty() << FirstModule << Range
+               << DiffType;
+      };
+      auto ODRDiagNote = [&SecondModule, this](
+          SourceLocation Loc, SourceRange Range, ODRDeclDifference DiffType) {
+        return Diag(Loc, diag::note_module_odr_violation_mismatch_decl_diff)
+               << SecondModule << Range << DiffType;
+      };
+
+      auto ComputeODRHash = [&Hash](const Stmt* S) {
+        assert(S);
+        Hash.clear();
+        Hash.AddStmt(S);
+        return Hash.CalculateHash();
+      };
+
+      switch (FirstDiffType) {
+      case Other:
+      case EndOfClass:
+      case PublicSpecifer:
+      case PrivateSpecifer:
+      case ProtectedSpecifer:
+        llvm_unreachable("Invalid diff type");
+
+      case StaticAssert: {
+        StaticAssertDecl *FirstSA = cast<StaticAssertDecl>(FirstDecl);
+        StaticAssertDecl *SecondSA = cast<StaticAssertDecl>(SecondDecl);
+
+        Expr *FirstExpr = FirstSA->getAssertExpr();
+        Expr *SecondExpr = SecondSA->getAssertExpr();
+        unsigned FirstODRHash = ComputeODRHash(FirstExpr);
+        unsigned SecondODRHash = ComputeODRHash(SecondExpr);
+        if (FirstODRHash != SecondODRHash) {
+          ODRDiagError(FirstExpr->getLocStart(), FirstExpr->getSourceRange(),
+                       StaticAssertCondition);
+          ODRDiagNote(SecondExpr->getLocStart(),
+                      SecondExpr->getSourceRange(), StaticAssertCondition);
+          Diagnosed = true;
+          break;
+        }
+
+        StringLiteral *FirstStr = FirstSA->getMessage();
+        StringLiteral *SecondStr = SecondSA->getMessage();
+        assert((FirstStr || SecondStr) && "Both messages cannot be empty");
+        if ((FirstStr && !SecondStr) || (!FirstStr && SecondStr)) {
+          SourceLocation FirstLoc, SecondLoc;
+          SourceRange FirstRange, SecondRange;
+          if (FirstStr) {
+            FirstLoc = FirstStr->getLocStart();
+            FirstRange = FirstStr->getSourceRange();
+          } else {
+            FirstLoc = FirstSA->getLocStart();
+            FirstRange = FirstSA->getSourceRange();
+          }
+          if (SecondStr) {
+            SecondLoc = SecondStr->getLocStart();
+            SecondRange = SecondStr->getSourceRange();
+          } else {
+            SecondLoc = SecondSA->getLocStart();
+            SecondRange = SecondSA->getSourceRange();
+          }
+          ODRDiagError(FirstLoc, FirstRange, StaticAssertOnlyMessage)
+              << (FirstStr == nullptr);
+          ODRDiagNote(SecondLoc, SecondRange, StaticAssertOnlyMessage)
+              << (SecondStr == nullptr);
+          Diagnosed = true;
+          break;
+        }
+
+        if (FirstStr && SecondStr &&
+            FirstStr->getString() != SecondStr->getString()) {
+          ODRDiagError(FirstStr->getLocStart(), FirstStr->getSourceRange(),
+                       StaticAssertMessage);
+          ODRDiagNote(SecondStr->getLocStart(), SecondStr->getSourceRange(),
+                      StaticAssertMessage);
+          Diagnosed = true;
+          break;
+        }
+        break;
+      }
+      }
+
       if (Diagnosed == true)
         continue;
 

Modified: cfe/trunk/test/Modules/odr_hash.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/odr_hash.cpp?rev=295890&r1=295889&r2=295890&view=diff
==============================================================================
--- cfe/trunk/test/Modules/odr_hash.cpp (original)
+++ cfe/trunk/test/Modules/odr_hash.cpp Wed Feb 22 16:22:42 2017
@@ -21,7 +21,7 @@
 // RUN: echo "}"                        >> %t/Inputs/module.map
 
 // Run test
-// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++11
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache -x c++ -I%t/Inputs -verify %s -std=c++1z
 
 #if !defined(FIRST) && !defined(SECOND)
 #include "first.h"
@@ -57,6 +57,64 @@ S2 s2;
 #endif
 } // namespace AccessSpecifiers
 
+namespace StaticAssert {
+#if defined(FIRST)
+struct S1 {
+  static_assert(1 == 1, "First");
+};
+#elif defined(SECOND)
+struct S1 {
+  static_assert(1 == 1, "Second");
+};
+#else
+S1 s1;
+// expected-error at second.h:* {{'StaticAssert::S1' has different definitions in different modules; first difference is definition in module 'SecondModule' found static assert with message}}
+// expected-note at first.h:* {{but in 'FirstModule' found static assert with different message}}
+#endif
+
+#if defined(FIRST)
+struct S2 {
+  static_assert(2 == 2, "Message");
+};
+#elif defined(SECOND)
+struct S2 {
+  static_assert(2 == 2);
+};
+#else
+S2 s2;
+// expected-error at second.h:* {{'StaticAssert::S2' has different definitions in different modules; first difference is definition in module 'SecondModule' found static assert with no message}}
+// expected-note at first.h:* {{but in 'FirstModule' found static assert with message}}
+#endif
+
+#if defined(FIRST)
+struct S3 {
+  static_assert(3 == 3, "Message");
+};
+#elif defined(SECOND)
+struct S3 {
+  static_assert(3 != 4, "Message");
+};
+#else
+S3 s3;
+// expected-error at second.h:* {{'StaticAssert::S3' has different definitions in different modules; first difference is definition in module 'SecondModule' found static assert with condition}}
+// expected-note at first.h:* {{but in 'FirstModule' found static assert with different condition}}
+#endif
+
+#if defined(FIRST)
+struct S4 {
+  static_assert(4 == 4, "Message");
+};
+#elif defined(SECOND)
+struct S4 {
+  public:
+};
+#else
+S4 s4;
+// expected-error at second.h:* {{'StaticAssert::S4' has different definitions in different modules; first difference is definition in module 'SecondModule' found public access specifier}}
+// expected-note at first.h:* {{but in 'FirstModule' found static assert}}
+#endif
+}
+
 // Naive parsing of AST can lead to cycles in processing.  Ensure
 // self-references don't trigger an endless cycles of AST node processing.
 namespace SelfReference {
@@ -90,12 +148,18 @@ struct S {
   public:
   private:
   protected:
+
+  static_assert(1 == 1, "Message");
+  static_assert(2 == 2);
 };
 #elif defined(SECOND)
 struct S {
   public:
   private:
   protected:
+
+  static_assert(1 == 1, "Message");
+  static_assert(2 == 2);
 };
 #else
 S s;
@@ -107,6 +171,9 @@ struct T {
   private:
   protected:
 
+  static_assert(1 == 1, "Message");
+  static_assert(2 == 2);
+
   private:
 };
 #elif defined(SECOND)
@@ -115,6 +182,9 @@ struct T {
   private:
   protected:
 
+  static_assert(1 == 1, "Message");
+  static_assert(2 == 2);
+
   public:
 };
 #else




More information about the cfe-commits mailing list