balazske created this revision.
Herald added subscribers: steakhal, martong, gamesh411, Szelethus, dkrupp.
Herald added a reviewer: a.sidorin.
Herald added a reviewer: shafik.
Herald added a reviewer: Szelethus.
Herald added a project: All.
balazske requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

The "in-class initializer" expression should be set in the field of a
default initialization expression before this expression node is created.
The `CXXDefaultInitExpr` objects are created after the AST is loaded and
at import not present in the "To" AST. And the in-class initializers of
the used fields can be missing too, these must be set at import.

This fixes a github issue #54061.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D120824

Files:
  clang/lib/AST/ASTImporter.cpp
  clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp
  clang/test/Analysis/ctu-cxxdefaultinitexpr.cpp
  clang/unittests/AST/ASTImporterTest.cpp

Index: clang/unittests/AST/ASTImporterTest.cpp
===================================================================
--- clang/unittests/AST/ASTImporterTest.cpp
+++ clang/unittests/AST/ASTImporterTest.cpp
@@ -530,6 +530,21 @@
                            has(floatLiteral(equals(1.0)))))))));
 }
 
+const internal::VariadicDynCastAllOfMatcher<Expr, CXXDefaultInitExpr>
+    cxxDefaultInitExpr;
+
+TEST_P(ImportExpr, ImportCXXDefaultInitExpr) {
+  MatchVerifier<Decl> Verifier;
+  testImport("class declToImport { int DefInit = 5; }; declToImport X;",
+             Lang_CXX11, "", Lang_CXX11, Verifier,
+             cxxRecordDecl(hasDescendant(cxxConstructorDecl(
+                 hasAnyConstructorInitializer(cxxCtorInitializer(
+                     withInitializer(cxxDefaultInitExpr())))))));
+  testImport(
+      "struct X { int A = 5; }; X declToImport{};", Lang_CXX17, "", Lang_CXX17,
+      Verifier,
+      varDecl(hasInitializer(initListExpr(hasInit(0, cxxDefaultInitExpr())))));
+}
 
 const internal::VariadicDynCastAllOfMatcher<Expr, VAArgExpr> vaArgExpr;
 
Index: clang/test/Analysis/ctu-cxxdefaultinitexpr.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/ctu-cxxdefaultinitexpr.cpp
@@ -0,0 +1,35 @@
+// RUN: rm -rf %t && mkdir %t
+// RUN: clang-extdef-mapping %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp -- -std=c++17 > %t/externalDefMap.txt
+// RUN: sed -i "s:%S/Inputs/ctu-cxxdefaultinitexpr-import.cpp:ctu-cxxdefaultinitexpr-import.cpp.ast:g" %t/externalDefMap.txt
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++17 \
+// RUN:   -emit-pch -o %t/ctu-cxxdefaultinitexpr-import.cpp.ast %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -std=c++17 -analyze \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -verify %s
+
+// Check that importing this code does not cause crash.
+// expected-no-diagnostics
+
+namespace QHashPrivate {
+template <typename> int b;
+struct Data;
+} // namespace QHashPrivate
+
+struct QDomNodePrivate {};
+template <typename = struct QString> struct QMultiHash {
+  QHashPrivate::Data *d = nullptr;
+};
+
+struct QDomNamedNodeMapPrivate {
+  QMultiHash<> map;
+};
+struct QDomElementPrivate : QDomNodePrivate {
+  QDomElementPrivate();
+  void importee();
+  QMultiHash<> *m_attr = nullptr;
+};
+// --------- common part end ---------
+
+void importer(QDomElementPrivate x) { x.importee(); }
Index: clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp
@@ -0,0 +1,26 @@
+namespace QHashPrivate {
+template <typename> int b;
+struct Data;
+} // namespace QHashPrivate
+
+struct QDomNodePrivate {};
+template <typename = struct QString> struct QMultiHash {
+  QHashPrivate::Data *d = nullptr;
+};
+
+struct QDomNamedNodeMapPrivate {
+  QMultiHash<> map;
+};
+struct QDomElementPrivate : QDomNodePrivate {
+  QDomElementPrivate();
+  void importee();
+  QMultiHash<> *m_attr = nullptr;
+};
+// --------- common part end ---------
+
+QDomElementPrivate::QDomElementPrivate() : m_attr{new QMultiHash<>} {}
+void QDomElementPrivate::importee() { (void)QMultiHash<>{}; }
+struct foo {
+  QDomElementPrivate m = {};
+  static const int value = (QHashPrivate::b<foo>, 22);
+};
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -3646,19 +3646,23 @@
         // initializer of a FieldDecl might not had been instantiated in the
         // "To" context.  However, the "From" context might instantiated that,
         // thus we have to merge that.
+        // Note: `hasInClassInitializer()` is not the same as non-null
+        // `getInClassInitializer()` value.
         if (Expr *FromInitializer = D->getInClassInitializer()) {
-          // We don't have yet the initializer set.
-          if (FoundField->hasInClassInitializer() &&
-              !FoundField->getInClassInitializer()) {
-            if (ExpectedExpr ToInitializerOrErr = import(FromInitializer))
+          if (ExpectedExpr ToInitializerOrErr = import(FromInitializer)) {
+            // Import of the FromInitializer may result in the setting of
+            // InClassInitializer. If not, set it here.
+            assert(FoundField->hasInClassInitializer() &&
+                   "Field should have an in-class initializer if it has an "
+                   "expression for it.");
+            if (!FoundField->getInClassInitializer())
               FoundField->setInClassInitializer(*ToInitializerOrErr);
-            else {
-              // We can't return error here,
-              // since we already mapped D as imported.
-              // FIXME: warning message?
-              consumeError(ToInitializerOrErr.takeError());
-              return FoundField;
-            }
+          } else {
+            // We can't return error here,
+            // since we already mapped D as imported.
+            // FIXME: warning message?
+            consumeError(ToInitializerOrErr.takeError());
+            return FoundField;
           }
         }
         return FoundField;
@@ -8127,8 +8131,23 @@
   if (!UsedContextOrErr)
     return UsedContextOrErr.takeError();
 
-  return CXXDefaultInitExpr::Create(
-      Importer.getToContext(), *ToBeginLocOrErr, *ToFieldOrErr, *UsedContextOrErr);
+  FieldDecl *ToField = *ToFieldOrErr;
+  assert(ToField->hasInClassInitializer() &&
+         "Field should have in-class initializer if there is a default init "
+         "expression that uses it.");
+  if (!ToField->getInClassInitializer()) {
+    // The in-class initializer may be not yet set in "To" AST even if the
+    // field is already there. This must be set here to make construction of
+    // CXXDefaultInitExpr work.
+    auto ToInClassInitializerOrErr =
+        import(E->getField()->getInClassInitializer());
+    if (!ToInClassInitializerOrErr)
+      return ToInClassInitializerOrErr.takeError();
+    ToField->setInClassInitializer(*ToInClassInitializerOrErr);
+  }
+
+  return CXXDefaultInitExpr::Create(Importer.getToContext(), *ToBeginLocOrErr,
+                                    ToField, *UsedContextOrErr);
 }
 
 ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to