[clang] f443838 - [clang][ASTImporter] Fix import of recursive field initializer.
Balázs Kéri via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 27 00:35:12 PDT 2023
Author: Balázs Kéri
Date: 2023-07-27T09:34:34+02:00
New Revision: f4438385d4d9b7e652b41f908250e55f75695ab6
URL: https://github.com/llvm/llvm-project/commit/f4438385d4d9b7e652b41f908250e55f75695ab6
DIFF: https://github.com/llvm/llvm-project/commit/f4438385d4d9b7e652b41f908250e55f75695ab6.diff
LOG: [clang][ASTImporter] Fix import of recursive field initializer.
Import of field initializers with circular reference was not working,
this is fixed now.
Fixes issue #63120
Reviewed By: steakhal
Differential Revision: https://reviews.llvm.org/D155574
Added:
Modified:
clang/lib/AST/ASTImporter.cpp
clang/unittests/AST/ASTImporterTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 39c7a8fa397048..eb8e28ccd3e622 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -3925,7 +3925,6 @@ ExpectedDecl ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
auto ToTInfo = importChecked(Err, D->getTypeSourceInfo());
auto ToBitWidth = importChecked(Err, D->getBitWidth());
auto ToInnerLocStart = importChecked(Err, D->getInnerLocStart());
- auto ToInitializer = importChecked(Err, D->getInClassInitializer());
if (Err)
return std::move(Err);
const Type *ToCapturedVLAType = nullptr;
@@ -3948,12 +3947,24 @@ ExpectedDecl ASTNodeImporter::VisitFieldDecl(FieldDecl *D) {
return std::move(Err);
ToField->setAccess(D->getAccess());
ToField->setLexicalDeclContext(LexicalDC);
- if (ToInitializer)
- ToField->setInClassInitializer(ToInitializer);
ToField->setImplicit(D->isImplicit());
if (ToCapturedVLAType)
ToField->setCapturedVLAType(cast<VariableArrayType>(ToCapturedVLAType));
LexicalDC->addDeclInternal(ToField);
+ // Import initializer only after the field was created, it may have recursive
+ // reference to the field.
+ auto ToInitializer = importChecked(Err, D->getInClassInitializer());
+ if (Err)
+ return std::move(Err);
+ if (ToInitializer) {
+ auto *AlreadyImported = ToField->getInClassInitializer();
+ if (AlreadyImported)
+ assert(ToInitializer == AlreadyImported &&
+ "Duplicate import of in-class initializer.");
+ else
+ ToField->setInClassInitializer(ToInitializer);
+ }
+
return ToField;
}
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 3a1058f5e3fe90..c6ae2693930409 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -8165,6 +8165,83 @@ TEST_P(ASTImporterOptionSpecificTestBase,
EXPECT_TRUE(ToX->getInClassInitializer());
}
+TEST_P(ASTImporterOptionSpecificTestBase, ImportRecursiveFieldInitializer) {
+ const char *Code =
+ R"(
+ struct AP_TECS;
+
+ struct AP_Landing {
+ AP_TECS *TECS_controller;
+ };
+
+ struct AP_TECS {
+ AP_Landing landing;
+ };
+
+ class Plane {
+ AP_TECS TECS_controller{landing};
+ AP_Landing landing{&TECS_controller};
+ };
+ )";
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+
+ auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("Plane")));
+ for (FieldDecl *F : FromR->fields())
+ EXPECT_TRUE(F->getInClassInitializer());
+ auto *ToR = Import(FromR, Lang_CXX11);
+ for (FieldDecl *F : ToR->fields())
+ EXPECT_TRUE(F->getInClassInitializer());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportFieldInitializerWithItself) {
+ const char *Code =
+ R"(
+ class A {
+ int a{a};
+ };
+ )";
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A")));
+ EXPECT_TRUE(FromA->field_begin()->getInClassInitializer());
+ auto *ToA = Import(FromA, Lang_CXX11);
+ EXPECT_TRUE(ToA->field_begin()->getInClassInitializer());
+}
+
+TEST_P(ASTImporterOptionSpecificTestBase, ImportRecursiveFieldInitializer1) {
+ // FIXME: This is a example of recursive field initialization that is not
+ // supported.
+ // The following import chain occurs (not complete):
+ // import of A => A.a => in-class initializer of A.a => ref_B() => B => B.b
+ // => in-class initializer of B.b => ref_A() => CXXConstructExpr for A =>
+ // CXXDefaultInitExpr for A.a => in-class initializer of A.a
+ // in-class initializer of A.a is created in two
diff erent instances in this
+ // case (import of FieldDecl and CXXDefaultInitExpr). Probably not a big
+ // problem because it is an Expr (the second construction can be ignored
+ // instead of assert). But such recursive init code should not occur in
+ // practice.
+ const char *Code =
+ R"(
+ static int ref_A();
+ static int ref_B();
+ struct A {
+ int a = ref_B();
+ };
+ struct B {
+ int b = ref_A();
+ };
+ int ref_B() { B b; return b.b; }
+ int ref_A() { A a; return a.a; }
+ )";
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("A")));
+ EXPECT_TRUE(FromA->field_begin()->getInClassInitializer());
+ // auto *ToA = Import(FromA, Lang_CXX11);
+ // EXPECT_TRUE(ToA->field_begin()->getInClassInitializer());
+}
+
TEST_P(ASTImporterOptionSpecificTestBase, isNewDecl) {
Decl *FromTU = getTuDecl(
R"(
More information about the cfe-commits
mailing list