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

--

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


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.

5 participants