Skip to content

Conversation

balazske
Copy link
Collaborator

When a type is imported with ASTImporter, the "original declaration" of the type is imported. In some cases this is not the definition (of the class). Before the fix the definition was only imported if there was an other reference to it in the AST to import. This is not always the case (like in the added test case), if not the definition was missing in the "To" AST which can cause the assertion later.

…uring CTU static analysis

When a type is imported with `ASTImporter`, the "original declaration"
of the type is imported. In some cases this is not the definition
(of the class). Before the fix the definition was only imported if
there was an other reference to it in the AST to import. This is not
always the case (like in the added test case), if not the definition
was missing in the "To" AST which can cause the assertion later.
@balazske balazske requested a review from shafik August 26, 2025 08:55
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:static analyzer labels Aug 26, 2025
@balazske balazske requested a review from Michael137 August 26, 2025 08:55
@llvmbot
Copy link
Member

llvmbot commented Aug 26, 2025

@llvm/pr-subscribers-clang-static-analyzer-1

@llvm/pr-subscribers-clang

Author: Balázs Kéri (balazske)

Changes

When a type is imported with ASTImporter, the "original declaration" of the type is imported. In some cases this is not the definition (of the class). Before the fix the definition was only imported if there was an other reference to it in the AST to import. This is not always the case (like in the added test case), if not the definition was missing in the "To" AST which can cause the assertion later.


Full diff: https://github.com/llvm/llvm-project/pull/155375.diff

3 Files Affected:

  • (modified) clang/lib/AST/ASTImporter.cpp (+12-1)
  • (added) clang/test/Analysis/ctu-import-type-decl-definition.c (+43)
  • (modified) clang/unittests/AST/ASTImporterTest.cpp (+2-1)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 6299efaf6bbfc..b4c9cf699fd9c 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1740,10 +1740,21 @@ ExpectedType ASTNodeImporter::VisitDeducedTemplateSpecializationType(
 }
 
 ExpectedType ASTNodeImporter::VisitTagType(const TagType *T) {
-  Expected<TagDecl *> ToDeclOrErr = import(T->getOriginalDecl());
+  TagDecl *DeclForType = T->getOriginalDecl();
+  Expected<TagDecl *> ToDeclOrErr = import(DeclForType);
   if (!ToDeclOrErr)
     return ToDeclOrErr.takeError();
 
+  // If there is a definition of the 'OriginalDecl', it should be imported to
+  // have all information for the type in the "To" AST. (In rare cases no other
+  // reference may exist to the definition and it would not be imported
+  // otherwise.)
+  if (TagDecl *DefDecl = DeclForType->getDefinition()) {
+    Expected<TagDecl *> ToDefDeclOrErr = import(DefDecl);
+    if (!ToDefDeclOrErr)
+      return ToDefDeclOrErr.takeError();
+  }
+
   if (T->isCanonicalUnqualified())
     return Importer.getToContext().getCanonicalTagType(*ToDeclOrErr);
 
diff --git a/clang/test/Analysis/ctu-import-type-decl-definition.c b/clang/test/Analysis/ctu-import-type-decl-definition.c
new file mode 100644
index 0000000000000..4610ff8268555
--- /dev/null
+++ b/clang/test/Analysis/ctu-import-type-decl-definition.c
@@ -0,0 +1,43 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -emit-pch -o %t/import.ast %t/import.c
+
+// RUN: %clang_cc1 -analyze \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN:   -analyzer-config display-ctu-progress=true \
+// RUN:   -analyzer-config ctu-dir=%t \
+// RUN:   -verify %t/main.c
+
+//--- main.c
+
+// expected-no-diagnostics
+
+typedef struct X_s X_t;
+unsigned long f_import(struct X_s *xPtr);
+
+static void freeWriteFileResources(struct X_s *xPtr) {
+  f_import(xPtr);
+}
+
+//--- import.c
+
+typedef struct Y_s Y_t;
+
+struct Y_s {
+};
+
+struct X_s {
+  Y_t y;
+};
+
+unsigned long f_import(struct X_s *xPtr) {
+  if (xPtr != 0) {
+  }
+  return 0;
+}
+
+//--- externalDefMap.txt
+13:c:@F@f_import import.ast
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 15df9af889386..49bdc591dda92 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -10027,7 +10027,8 @@ struct ImportTemplateParmDeclDefaultValue
       EXPECT_EQ(ToD->getPreviousDecl(), ToDInherited);
     } else {
       EXPECT_EQ(FromD, FromDInherited->getPreviousDecl());
-      EXPECT_EQ(ToD, ToDInherited->getPreviousDecl());
+      // The order becomes reversed by the import process.
+      EXPECT_EQ(ToD->getPreviousDecl(), ToDInherited);
     }
   }
 

Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I only had a tiny remark inline.

}

//--- externalDefMap.txt
13:c:@F@f_import import.ast
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please generate this in the RUN lines?
If we decide to update the USR generation, this would break.
As this test does not care about USR generation and its correctness, that would be just noise.

Comment on lines 1753 to 1754
Expected<TagDecl *> ToDefDeclOrErr = import(DefDecl);
if (!ToDefDeclOrErr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Expected<TagDecl *> ToDefDeclOrErr = import(DefDecl);
if (!ToDefDeclOrErr)
if (Expected<TagDecl *> ToDefDeclOrErr = import(DefDecl); !ToDefDeclOrErr)

@steakhal
Copy link
Contributor

BTW this made me think of lazy loaded decls. (see clang::ExternalASTSource and noload_decls and decls of a DeclContext). Maybe we could delay the import of the definition decl until we need it using some abstraction.
I think that would be likely really complicated though, but I still figured worth mentioning.

@balazske
Copy link
Collaborator Author

It is possible to set the DeclForType to the definition (if one exists) and import only this value. This would be only one import. The only issue is that later the value is used in getTagType instead of the original value (corresponding to imported getOriginalDecl) and I am not sure if this is entirely correct (but all tests pass in this case too).

@balazske
Copy link
Collaborator Author

balazske commented Aug 26, 2025

Probably isUsed() cal tell if import of definition is needed.

Copy link
Contributor

@mizvekov mizvekov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@balazske balazske merged commit b4fb9aa into llvm:main Aug 29, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Aug 29, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-aarch64-darwin running on doug-worker-5 while building clang at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/190/builds/26374

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'Clang :: Analysis/ctu-import-type-decl-definition.c' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
rm -rf /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp # RUN: at line 1
+ rm -rf /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp
mkdir -p /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp # RUN: at line 2
+ mkdir -p /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp
split-file /Users/buildbot/buildbot-root2/aarch64-darwin/llvm-project/clang/test/Analysis/ctu-import-type-decl-definition.c /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp # RUN: at line 3
+ split-file /Users/buildbot/buildbot-root2/aarch64-darwin/llvm-project/clang/test/Analysis/ctu-import-type-decl-definition.c /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp
/Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/bin/clang -cc1 -internal-isystem /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/lib/clang/22/include -nostdsysteminc -emit-pch -o /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c.ast /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c # RUN: at line 5
+ /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/bin/clang -cc1 -internal-isystem /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/lib/clang/22/include -nostdsysteminc -emit-pch -o /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c.ast /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c
/Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/bin/clang-extdef-mapping -- -x c /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c >> /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/externalDefMap.txt # RUN: at line 7
+ /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/bin/clang-extdef-mapping -- -x c /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/import.c
sed -i 's/$/.ast/' /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/externalDefMap.txt # RUN: at line 8
+ sed -i 's/$/.ast/' /Volumes/ExternalSSD/buildbot-root/aarch64-darwin/build/tools/clang/test/Analysis/Output/ctu-import-type-decl-definition.c.tmp/externalDefMap.txt
sed: 1: "/Volumes/ExternalSSD/bu ...": invalid command code E

--

********************


@dyung
Copy link
Collaborator

dyung commented Aug 29, 2025

LLVM Buildbot has detected a new failure on builder llvm-clang-aarch64-darwin running on doug-worker-5 while building clang at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/190/builds/26374

Here is the relevant piece of the build log for the reference

@balazske, the test you added seems to not handle paths on MacOS with forward slashes in them correctly. It seems like sed might be trying to interpret them mistakenly instead of treating it as a path. Can you take a look and fix it so we can get the bot back to green?

@balazske
Copy link
Collaborator Author

I can not test on MacOS so it takes longer to have a fix for it.

balazske added a commit that referenced this pull request Aug 29, 2025
…ions!' during CTU static analysis" (#156010)

Reverts #155375
Test does not work correctly on MacOS.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Aug 29, 2025
…rd declarations!' during CTU static analysis" (#156010)

Reverts llvm/llvm-project#155375
Test does not work correctly on MacOS.
@balazske
Copy link
Collaborator Author

I have figured out how (hopefully) fix the sed command error but it looks like a new pull request is needed (not possible to repoen this one).

@balazske
Copy link
Collaborator Author

#156056 is the new PR (but I can not verify if this really works).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants