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