https://github.com/ChuanqiXu9 created 
https://github.com/llvm/llvm-project/pull/217836

Close https://github.com/llvm/llvm-project/issues/214982

which is invalid example but users thought it is not. We should give better 
diagnostic messages

>From 6e6642d658cce727c59368897c41804a39c2d375 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Fri, 21 Aug 2026 15:15:23 +0800
Subject: [PATCH] [C++20] [Modules] Diagnose instantiation of templates
 including TULocal entities

Close https://github.com/llvm/llvm-project/issues/214982

which is invalid example but users thought it is not. We should
give better diagnostic messages
---
 clang/lib/Sema/SemaModule.cpp    | 31 +++++++++++++-----
 clang/test/Modules/pr214982.cppm | 56 ++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/Modules/pr214982.cppm

diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index 8a7bf172c8342..d7a182fe5654c 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -1453,7 +1453,7 @@ bool ExposureChecker::checkExposure(const CXXRecordDecl 
*RD, bool Diag) {
 
 class ReferenceTULocalChecker : public DynamicRecursiveASTVisitor {
 public:
-  using CallbackTy = std::function<void(DeclRefExpr *, ValueDecl *)>;
+  using CallbackTy = std::function<void(SourceLocation, NamedDecl *)>;
 
   ReferenceTULocalChecker(ExposureChecker &C, CallbackTy &&Callback)
       : Checker(C), Callback(std::move(Callback)) {}
@@ -1489,7 +1489,14 @@ class ReferenceTULocalChecker : public 
DynamicRecursiveASTVisitor {
           VD->getInit()->isConstantInitializer(Context))
         return true;
 
-    Callback(DRE, Referenced);
+    Callback(DRE->getExprLoc(), Referenced);
+    return true;
+  }
+
+  bool VisitTagTypeLoc(TagTypeLoc TL) override {
+    TagDecl *Referenced = TL.getDecl();
+    if (Checker.isTULocal(Referenced))
+      Callback(TL.getNameLoc(), Referenced);
     return true;
   }
 
@@ -1503,10 +1510,10 @@ bool ExposureChecker::checkExposure(const Stmt *S, bool 
Diag) {
 
   bool HasReferencedTULocals = false;
   ReferenceTULocalChecker Checker(
-      *this, [this, &HasReferencedTULocals, Diag](DeclRefExpr *DRE,
-                                                  ValueDecl *Referenced) {
+      *this, [this, &HasReferencedTULocals, Diag](SourceLocation Loc,
+                                                  NamedDecl *Referenced) {
         if (Diag) {
-          SemaRef.Diag(DRE->getExprLoc(), diag::warn_exposure) << Referenced;
+          SemaRef.Diag(Loc, diag::warn_exposure) << Referenced;
         }
         HasReferencedTULocals = true;
       });
@@ -1566,11 +1573,17 @@ void Sema::checkExposure(const TranslationUnitDecl *TU) 
{
     FunctionDecl *FD = FDAndInstantiationLocPair.first;
     SourceLocation PointOfInstantiation = FDAndInstantiationLocPair.second;
 
-    if (!FD->hasBody())
+    // Substitution may fail before an instantiated body is formed. The pattern
+    // still contains non-dependent references to TU-local entities, use the
+    // instantiation pattern as the body.
+    const FunctionDecl *BodyOwner = FD;
+    if (!BodyOwner->hasBody())
+      BodyOwner = FD->getTemplateInstantiationPattern();
+    if (!BodyOwner || !BodyOwner->hasBody())
       continue;
 
-    ReferenceTULocalChecker(Checker, [&, this](DeclRefExpr *DRE,
-                                               ValueDecl *Referenced) {
+    ReferenceTULocalChecker(Checker, [&, this](SourceLocation,
+                                               NamedDecl *Referenced) {
       // A "defect" in current implementation. Now an implicit instantiation of
       // a template, the instantiation is considered to be in the same module
       // unit as the template instead of the module unit where the 
instantiation
@@ -1595,7 +1608,7 @@ void Sema::checkExposure(const TranslationUnitDecl *TU) {
            diag::warn_reference_tu_local_entity_in_other_tu)
           << FD << Referenced
           << Referenced->getOwningModule()->getTopLevelModuleName();
-    }).TraverseStmt(FD->getBody());
+    }).TraverseStmt(BodyOwner->getBody());
   }
 }
 
diff --git a/clang/test/Modules/pr214982.cppm b/clang/test/Modules/pr214982.cppm
new file mode 100644
index 0000000000000..39bbc328fa2af
--- /dev/null
+++ b/clang/test/Modules/pr214982.cppm
@@ -0,0 +1,56 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/part.cppm -o 
%t/part.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/repro.cppm \
+// RUN:   -fmodule-file=repro:part=%t/part.pcm -o %t/repro.pcm
+//
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/valid.cpp \
+// RUN:   -fmodule-file=repro=%t/repro.pcm \
+// RUN:   -fmodule-file=repro:part=%t/part.pcm 2>&1 -verify
+//
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/main.cpp \
+// RUN:   -fmodule-file=repro=%t/repro.pcm \
+// RUN:   -fmodule-file=repro:part=%t/part.pcm 2>&1 -verify
+
+
+//--- part.cppm
+export module repro:part;
+
+namespace {
+struct guard_t {
+  guard_t(int &x) : x{x} { ++this->x; }
+  ~guard_t() { --this->x; }
+  int &x;
+};
+}
+
+export template <typename T>
+struct widget_t {
+  void bump() const { guard_t g{this->x}; }
+  void fine() const { }
+  mutable int x = 0;
+};
+
+//--- repro.cppm
+export module repro;
+export import :part;
+
+//--- valid.cpp
+// expected-no-diagnostics
+import repro;
+
+void valid() {
+  widget_t<int> w;
+  w.fine();
+}
+
+//--- main.cpp
+import repro;
+
+int main() {
+  widget_t<int> w;
+            // [email protected]:13 {{no matching constructor for 
initialization of 'guard_t'}}
+  w.bump(); // expected-warning {{instantiation of 'bump' triggers reference 
to TU-local entity 'guard_t' from other TU 'repro:part'}}
+            // expected-note@-1 {{in instantiation of member function 
'widget_t<int>::bump' requested here}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to