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
