Skip to content

Commit c5d83cd

Browse files
committed
[clang][ASTImporter] Fix a bug when importing CXXDefaultInitExpr.
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. Reviewed By: martong Differential Revision: https://reviews.llvm.org/D120824
1 parent fcbf00f commit c5d83cd

File tree

5 files changed

+198
-13
lines changed

5 files changed

+198
-13
lines changed

clang/lib/AST/ASTImporter.cpp

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3646,19 +3646,23 @@ ExpectedDecl ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
36463646
// initializer of a FieldDecl might not had been instantiated in the
36473647
// "To" context. However, the "From" context might instantiated that,
36483648
// thus we have to merge that.
3649+
// Note: `hasInClassInitializer()` is not the same as non-null
3650+
// `getInClassInitializer()` value.
36493651
if (Expr *FromInitializer = D->getInClassInitializer()) {
3650-
// We don't have yet the initializer set.
3651-
if (FoundField->hasInClassInitializer() &&
3652-
!FoundField->getInClassInitializer()) {
3653-
if (ExpectedExpr ToInitializerOrErr = import(FromInitializer))
3652+
if (ExpectedExpr ToInitializerOrErr = import(FromInitializer)) {
3653+
// Import of the FromInitializer may result in the setting of
3654+
// InClassInitializer. If not, set it here.
3655+
assert(FoundField->hasInClassInitializer() &&
3656+
"Field should have an in-class initializer if it has an "
3657+
"expression for it.");
3658+
if (!FoundField->getInClassInitializer())
36543659
FoundField->setInClassInitializer(*ToInitializerOrErr);
3655-
else {
3656-
// We can't return error here,
3657-
// since we already mapped D as imported.
3658-
// FIXME: warning message?
3659-
consumeError(ToInitializerOrErr.takeError());
3660-
return FoundField;
3661-
}
3660+
} else {
3661+
// We can't return error here,
3662+
// since we already mapped D as imported.
3663+
// FIXME: warning message?
3664+
consumeError(ToInitializerOrErr.takeError());
3665+
return FoundField;
36623666
}
36633667
}
36643668
return FoundField;
@@ -8127,8 +8131,23 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
81278131
if (!UsedContextOrErr)
81288132
return UsedContextOrErr.takeError();
81298133

8130-
return CXXDefaultInitExpr::Create(
8131-
Importer.getToContext(), *ToBeginLocOrErr, *ToFieldOrErr, *UsedContextOrErr);
8134+
FieldDecl *ToField = *ToFieldOrErr;
8135+
assert(ToField->hasInClassInitializer() &&
8136+
"Field should have in-class initializer if there is a default init "
8137+
"expression that uses it.");
8138+
if (!ToField->getInClassInitializer()) {
8139+
// The in-class initializer may be not yet set in "To" AST even if the
8140+
// field is already there. This must be set here to make construction of
8141+
// CXXDefaultInitExpr work.
8142+
auto ToInClassInitializerOrErr =
8143+
import(E->getField()->getInClassInitializer());
8144+
if (!ToInClassInitializerOrErr)
8145+
return ToInClassInitializerOrErr.takeError();
8146+
ToField->setInClassInitializer(*ToInClassInitializerOrErr);
8147+
}
8148+
8149+
return CXXDefaultInitExpr::Create(Importer.getToContext(), *ToBeginLocOrErr,
8150+
ToField, *UsedContextOrErr);
81328151
}
81338152

81348153
ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace QHashPrivate {
2+
template <typename> int b;
3+
struct Data;
4+
} // namespace QHashPrivate
5+
6+
struct QDomNodePrivate {};
7+
template <typename = struct QString> struct QMultiHash {
8+
QHashPrivate::Data *d = nullptr;
9+
};
10+
11+
struct QDomNamedNodeMapPrivate {
12+
QMultiHash<> map;
13+
};
14+
struct QDomElementPrivate : QDomNodePrivate {
15+
QDomElementPrivate();
16+
void importee();
17+
QMultiHash<> *m_attr = nullptr;
18+
};
19+
// --------- common part end ---------
20+
21+
QDomElementPrivate::QDomElementPrivate() : m_attr{new QMultiHash<>} {}
22+
void QDomElementPrivate::importee() { (void)QMultiHash<>{}; }
23+
struct foo {
24+
QDomElementPrivate m = {};
25+
static const int value = (QHashPrivate::b<foo>, 22);
26+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
14:c:@S@foo@value ctu-cxxdefaultinitexpr-import.cpp.ast
2+
35:c:@S@QDomElementPrivate@F@importee# ctu-cxxdefaultinitexpr-import.cpp.ast
3+
45:c:@S@QDomElementPrivate@F@QDomElementPrivate# ctu-cxxdefaultinitexpr-import.cpp.ast
4+
39:c:@S@QDomNodePrivate@F@QDomNodePrivate# ctu-cxxdefaultinitexpr-import.cpp.ast
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: rm -rf %t && mkdir %t
2+
// RUN: mkdir -p %t/ctudir
3+
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++17 \
4+
// RUN: -emit-pch -o %t/ctudir/ctu-cxxdefaultinitexpr-import.cpp.ast %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp
5+
// RUN: cp %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt
6+
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -std=c++17 -analyze \
7+
// RUN: -analyzer-checker=core \
8+
// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \
9+
// RUN: -analyzer-config ctu-dir=%t/ctudir \
10+
// RUN: -verify %s
11+
12+
// Check that importing this code does not cause crash.
13+
// expected-no-diagnostics
14+
15+
namespace QHashPrivate {
16+
template <typename> int b;
17+
struct Data;
18+
} // namespace QHashPrivate
19+
20+
struct QDomNodePrivate {};
21+
template <typename = struct QString> struct QMultiHash {
22+
QHashPrivate::Data *d = nullptr;
23+
};
24+
25+
struct QDomNamedNodeMapPrivate {
26+
QMultiHash<> map;
27+
};
28+
struct QDomElementPrivate : QDomNodePrivate {
29+
QDomElementPrivate();
30+
void importee();
31+
QMultiHash<> *m_attr = nullptr;
32+
};
33+
// --------- common part end ---------
34+
35+
void importer(QDomElementPrivate x) { x.importee(); }

clang/unittests/AST/ASTImporterTest.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,21 @@ TEST_P(ImportExpr, ImportInitListExpr) {
530530
has(floatLiteral(equals(1.0)))))))));
531531
}
532532

533+
const internal::VariadicDynCastAllOfMatcher<Expr, CXXDefaultInitExpr>
534+
cxxDefaultInitExpr;
535+
536+
TEST_P(ImportExpr, ImportCXXDefaultInitExpr) {
537+
MatchVerifier<Decl> Verifier;
538+
testImport("class declToImport { int DefInit = 5; }; declToImport X;",
539+
Lang_CXX11, "", Lang_CXX11, Verifier,
540+
cxxRecordDecl(hasDescendant(cxxConstructorDecl(
541+
hasAnyConstructorInitializer(cxxCtorInitializer(
542+
withInitializer(cxxDefaultInitExpr())))))));
543+
testImport(
544+
"struct X { int A = 5; }; X declToImport{};", Lang_CXX17, "", Lang_CXX17,
545+
Verifier,
546+
varDecl(hasInitializer(initListExpr(hasInit(0, cxxDefaultInitExpr())))));
547+
}
533548

534549
const internal::VariadicDynCastAllOfMatcher<Expr, VAArgExpr> vaArgExpr;
535550

@@ -7529,6 +7544,92 @@ TEST_P(ASTImporterOptionSpecificTestBase,
75297544
EXPECT_TRUE(ToA->isCompleteDefinition());
75307545
}
75317546

7547+
TEST_P(ASTImporterOptionSpecificTestBase, ImportInClassInitializerFromField) {
7548+
// Encounter import of a field when the field already exists but has the
7549+
// in-class initializer expression not yet set. Such case can occur in the AST
7550+
// of generated template specializations.
7551+
// The first code forces to create a template specialization of
7552+
// `A<int>` but without implicit constructors.
7553+
// The second ("From") code contains a variable of type `A<int>`, this
7554+
// results in a template specialization that has constructors and
7555+
// CXXDefaultInitExpr nodes.
7556+
Decl *ToTU = getToTuDecl(
7557+
R"(
7558+
void f();
7559+
template<typename> struct A { int X = 1; };
7560+
struct B { A<int> Y; };
7561+
)",
7562+
Lang_CXX11);
7563+
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
7564+
ToTU,
7565+
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
7566+
ASSERT_TRUE(ToX->hasInClassInitializer());
7567+
ASSERT_FALSE(ToX->getInClassInitializer());
7568+
7569+
Decl *FromTU = getTuDecl(
7570+
R"(
7571+
void f();
7572+
template<typename> struct A { int X = 1; };
7573+
struct B { A<int> Y; };
7574+
//
7575+
A<int> Z;
7576+
)",
7577+
Lang_CXX11, "input1.cc");
7578+
auto *FromX = FirstDeclMatcher<FieldDecl>().match(
7579+
FromTU,
7580+
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
7581+
7582+
auto *ToXImported = Import(FromX, Lang_CXX11);
7583+
EXPECT_EQ(ToXImported, ToX);
7584+
EXPECT_TRUE(ToX->getInClassInitializer());
7585+
}
7586+
7587+
TEST_P(ASTImporterOptionSpecificTestBase,
7588+
ImportInClassInitializerFromCXXDefaultInitExpr) {
7589+
// Encounter AST import of a CXXDefaultInitExpr where the "to-field"
7590+
// of it exists but has the in-class initializer not set yet.
7591+
Decl *ToTU = getToTuDecl(
7592+
R"(
7593+
namespace N {
7594+
template<typename> int b;
7595+
struct X;
7596+
}
7597+
template<typename> struct A { N::X *X = nullptr; };
7598+
struct B { A<int> Y; };
7599+
)",
7600+
Lang_CXX14);
7601+
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
7602+
ToTU,
7603+
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
7604+
ASSERT_TRUE(ToX->hasInClassInitializer());
7605+
ASSERT_FALSE(ToX->getInClassInitializer());
7606+
7607+
Decl *FromTU = getTuDecl(
7608+
R"(
7609+
namespace N {
7610+
template<typename> int b;
7611+
struct X;
7612+
}
7613+
template<typename> struct A { N::X *X = nullptr; };
7614+
struct B { A<int> Y; };
7615+
//
7616+
void f() {
7617+
(void)A<int>{};
7618+
}
7619+
struct C {
7620+
C(): attr(new A<int>{}){}
7621+
A<int> *attr;
7622+
const int value = N::b<C>;
7623+
};
7624+
)",
7625+
Lang_CXX14, "input1.cc");
7626+
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
7627+
FromTU, functionDecl(hasName("f"), isDefinition()));
7628+
auto *ToF = Import(FromF, Lang_CXX11);
7629+
EXPECT_TRUE(ToF);
7630+
EXPECT_TRUE(ToX->getInClassInitializer());
7631+
}
7632+
75327633
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterLookupTableTest,
75337634
DefaultTestValuesForRunOptions);
75347635

0 commit comments

Comments
 (0)