Skip to content

Commit

Permalink
Merge pull request #50 from daedaleanai/try-fix
Browse files Browse the repository at this point in the history
[clang][ASTImporter] Fix a bug when importing CXXDefaultInitExpr.
  • Loading branch information
finomen authored Nov 1, 2022
2 parents 6d18a8b + 219a4bf commit fd63927
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 13 deletions.
45 changes: 32 additions & 13 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3637,19 +3637,23 @@ ExpectedDecl ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
// 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;
Expand Down Expand Up @@ -8118,8 +8122,23 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
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) {
Expand Down
26 changes: 26 additions & 0 deletions clang/test/Analysis/Inputs/ctu-cxxdefaultinitexpr-import.cpp
Original file line number Diff line number Diff line change
@@ -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);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
14:c:@S@foo@value ctu-cxxdefaultinitexpr-import.cpp.ast
35:c:@S@QDomElementPrivate@F@importee# ctu-cxxdefaultinitexpr-import.cpp.ast
45:c:@S@QDomElementPrivate@F@QDomElementPrivate# ctu-cxxdefaultinitexpr-import.cpp.ast
39:c:@S@QDomNodePrivate@F@QDomNodePrivate# ctu-cxxdefaultinitexpr-import.cpp.ast
35 changes: 35 additions & 0 deletions clang/test/Analysis/ctu-cxxdefaultinitexpr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: rm -rf %t && mkdir %t
// RUN: mkdir -p %t/ctudir
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++17 \
// RUN: -emit-pch -o %t/ctudir/ctu-cxxdefaultinitexpr-import.cpp.ast %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp
// RUN: cp %S/Inputs/ctu-cxxdefaultinitexpr-import.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt
// 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/ctudir \
// 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(); }
152 changes: 152 additions & 0 deletions clang/unittests/AST/ASTImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,21 @@ TEST_P(ImportExpr, ImportInitListExpr) {
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;

Expand Down Expand Up @@ -7475,6 +7490,143 @@ TEST_P(ASTImporterOptionSpecificTestBase, ImportDeductionGuideDifferentOrder) {
ToDGOther);
}

TEST_P(ASTImporterOptionSpecificTestBase,
ImportRecordWithLayoutRequestingExpr) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
struct A {
int idx;
static void foo(A x) {
(void)&"text"[x.idx];
}
};
)",
Lang_CXX11);

auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));

// Test that during import of 'foo' the record layout can be obtained without
// crash.
auto *ToA = Import(FromA, Lang_CXX11);
EXPECT_TRUE(ToA);
EXPECT_TRUE(ToA->isCompleteDefinition());
}

TEST_P(ASTImporterOptionSpecificTestBase,
ImportRecordWithLayoutRequestingExprDifferentRecord) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
struct B;
struct A {
int idx;
B *b;
};
struct B {
static void foo(A x) {
(void)&"text"[x.idx];
}
};
)",
Lang_CXX11);

auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));

// Test that during import of 'foo' the record layout (of 'A') can be obtained
// without crash. It is not possible to have all of the fields of 'A' imported
// at that time (without big code changes).
auto *ToA = Import(FromA, Lang_CXX11);
EXPECT_TRUE(ToA);
EXPECT_TRUE(ToA->isCompleteDefinition());
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportInClassInitializerFromField) {
// Encounter import of a field when the field already exists but has the
// in-class initializer expression not yet set. Such case can occur in the AST
// of generated template specializations.
// The first code forces to create a template specialization of
// `A<int>` but without implicit constructors.
// The second ("From") code contains a variable of type `A<int>`, this
// results in a template specialization that has constructors and
// CXXDefaultInitExpr nodes.
Decl *ToTU = getToTuDecl(
R"(
void f();
template<typename> struct A { int X = 1; };
struct B { A<int> Y; };
)",
Lang_CXX11);
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
ToTU,
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
ASSERT_TRUE(ToX->hasInClassInitializer());
ASSERT_FALSE(ToX->getInClassInitializer());

Decl *FromTU = getTuDecl(
R"(
void f();
template<typename> struct A { int X = 1; };
struct B { A<int> Y; };
//
A<int> Z;
)",
Lang_CXX11, "input1.cc");
auto *FromX = FirstDeclMatcher<FieldDecl>().match(
FromTU,
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));

auto *ToXImported = Import(FromX, Lang_CXX11);
EXPECT_EQ(ToXImported, ToX);
EXPECT_TRUE(ToX->getInClassInitializer());
}

TEST_P(ASTImporterOptionSpecificTestBase,
ImportInClassInitializerFromCXXDefaultInitExpr) {
// Encounter AST import of a CXXDefaultInitExpr where the "to-field"
// of it exists but has the in-class initializer not set yet.
Decl *ToTU = getToTuDecl(
R"(
namespace N {
template<typename> int b;
struct X;
}
template<typename> struct A { N::X *X = nullptr; };
struct B { A<int> Y; };
)",
Lang_CXX14);
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
ToTU,
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
ASSERT_TRUE(ToX->hasInClassInitializer());
ASSERT_FALSE(ToX->getInClassInitializer());

Decl *FromTU = getTuDecl(
R"(
namespace N {
template<typename> int b;
struct X;
}
template<typename> struct A { N::X *X = nullptr; };
struct B { A<int> Y; };
//
void f() {
(void)A<int>{};
}
struct C {
C(): attr(new A<int>{}){}
A<int> *attr;
const int value = N::b<C>;
};
)",
Lang_CXX14, "input1.cc");
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f"), isDefinition()));
auto *ToF = Import(FromF, Lang_CXX11);
EXPECT_TRUE(ToF);
EXPECT_TRUE(ToX->getInClassInitializer());
}

INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterLookupTableTest,
DefaultTestValuesForRunOptions);

Expand Down

0 comments on commit fd63927

Please sign in to comment.