[Analyzer] Support bodyfarming libstdc++ implementation of std::call_once.

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

llvm-svn: 315508
diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp
index 81ad999..ae89350 100644
--- a/clang/lib/Analysis/BodyFarm.cpp
+++ b/clang/lib/Analysis/BodyFarm.cpp
@@ -108,7 +108,7 @@
 
   /// Returns a *first* member field of a record declaration with a given name.
   /// \return an nullptr if no member with such a name exists.
-  NamedDecl *findMemberField(const CXXRecordDecl *RD, StringRef Name);
+  ValueDecl *findMemberField(const RecordDecl *RD, StringRef Name);
 
 private:
   ASTContext &C;
@@ -234,7 +234,7 @@
       OK_Ordinary);
 }
 
-NamedDecl *ASTMaker::findMemberField(const CXXRecordDecl *RD, StringRef Name) {
+ValueDecl *ASTMaker::findMemberField(const RecordDecl *RD, StringRef Name) {
 
   CXXBasePaths Paths(
       /* FindAmbiguities=*/false,
@@ -246,7 +246,7 @@
   DeclContextLookupResult Decls = RD->lookup(DeclName);
   for (NamedDecl *FoundDecl : Decls)
     if (!FoundDecl->getDeclContext()->isFunctionOrMethod())
-      return FoundDecl;
+      return cast<ValueDecl>(FoundDecl);
 
   return nullptr;
 }
@@ -328,25 +328,31 @@
   const ParmVarDecl *Callback = D->getParamDecl(1);
   QualType CallbackType = Callback->getType().getNonReferenceType();
   QualType FlagType = Flag->getType().getNonReferenceType();
-  CXXRecordDecl *FlagCXXDecl = FlagType->getAsCXXRecordDecl();
-  if (!FlagCXXDecl) {
-    DEBUG(llvm::dbgs() << "Flag field is not a CXX record: "
-                       << "unknown std::call_once implementation."
-                       << "Ignoring the call.\n");
+  auto *FlagRecordDecl = dyn_cast_or_null<RecordDecl>(FlagType->getAsTagDecl());
+
+  if (!FlagRecordDecl) {
+    DEBUG(llvm::dbgs() << "Flag field is not a record: "
+                       << "unknown std::call_once implementation, "
+                       << "ignoring the call.\n");
     return nullptr;
   }
 
-  // Note: here we are assuming libc++ implementation of call_once,
-  // which has a struct with a field `__state_`.
-  // Body farming might not work for other `call_once` implementations.
-  NamedDecl *FoundDecl = M.findMemberField(FlagCXXDecl, "__state_");
-  ValueDecl *FieldDecl;
-  if (FoundDecl) {
-    FieldDecl = dyn_cast<ValueDecl>(FoundDecl);
-  } else {
+  // We initially assume libc++ implementation of call_once,
+  // where the once_flag struct has a field `__state_`.
+  ValueDecl *FlagFieldDecl = M.findMemberField(FlagRecordDecl, "__state_");
+
+  // Otherwise, try libstdc++ implementation, with a field
+  // `_M_once`
+  if (!FlagFieldDecl) {
     DEBUG(llvm::dbgs() << "No field __state_ found on std::once_flag struct, "
-                       << "unable to synthesize call_once body, ignoring "
-                       << "the call.\n");
+                       << "assuming libstdc++ implementation\n");
+    FlagFieldDecl = M.findMemberField(FlagRecordDecl, "_M_once");
+  }
+
+  if (!FlagFieldDecl) {
+    DEBUG(llvm::dbgs() << "No field _M_once found on std::once flag struct: "
+                       << "unknown std::call_once implementation, "
+                       << "ignoring the call");
     return nullptr;
   }
 
@@ -383,7 +389,7 @@
                         /* GetNonReferenceType=*/true);
 
 
-  MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FieldDecl);
+  MemberExpr *Deref = M.makeMemberExpression(FlagDecl, FlagFieldDecl);
   assert(Deref->isLValue());
   QualType DerefType = Deref->getType();