Author: Chuanqi Xu Date: 2026-09-04T16:07:43+08:00 New Revision: dcdd1bc13db6af3d2fbed6ebe3d296d28107e77f
URL: https://github.com/llvm/llvm-project/commit/dcdd1bc13db6af3d2fbed6ebe3d296d28107e77f DIFF: https://github.com/llvm/llvm-project/commit/dcdd1bc13db6af3d2fbed6ebe3d296d28107e77f.diff LOG: [C++20] [Modules] Identify the tag redecl through Using (#221147) Close https://github.com/llvm/llvm-project/issues/96423 We've already have some logics to merge the redecls. But if the previous decls are found through using decl, we can't merge them right now. As the reproducer shows. The using decl is common in modules as a common method to wrap headers into a module. The fix is simple too. If the existing check fails, we'll check if the found one is a using decl, if yes, we will reuse the existing check with the target decl for using decl. Added: clang/test/Modules/pr96423.cppm Modified: clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaTemplate.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9b07b591b8c07..2bfe58f02bc3a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4523,6 +4523,13 @@ class Sema final : public SemaBase { bool isDeclInScope(NamedDecl *D, DeclContext *Ctx, Scope *S = nullptr, bool AllowInlineNamespace = false) const; + /// Determine whether a tag-like declaration found by lookup can be + /// redeclared in the given scope. If lookup found a using-shadow, also + /// consider the scope of the declaration named by the using-shadow. + bool isTagRedeclarationInScope(NamedDecl *D, DeclContext *Ctx, + Scope *S = nullptr, + bool AllowInlineNamespace = false) const; + /// Finds the scope corresponding to the given decl context, if it /// happens to be an enclosing scope. Otherwise return NULL. static Scope *getScopeForDeclContext(Scope *S, DeclContext *DC); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d6114ccbae7fe..ccdc33d6fa39b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1658,6 +1658,17 @@ bool Sema::isDeclInScope(NamedDecl *D, DeclContext *Ctx, Scope *S, return IdResolver.isDeclInScope(D, Ctx, S, AllowInlineNamespace); } +bool Sema::isTagRedeclarationInScope(NamedDecl *D, DeclContext *Ctx, Scope *S, + bool AllowInlineNamespace) const { + if (isDeclInScope(D, Ctx, S, AllowInlineNamespace)) + return true; + + if (auto *Shadow = dyn_cast<UsingShadowDecl>(D)) + return isDeclInScope(Shadow->getTargetDecl(), Ctx, S, AllowInlineNamespace); + + return false; +} + Scope *Sema::getScopeForDeclContext(Scope *S, DeclContext *DC) { DeclContext *TargetDC = DC->getPrimaryContext(); do { @@ -18647,8 +18658,9 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, // in the same scope (so that the definition/declaration completes or // rementions the tag), reuse the decl. if (TUK == TagUseKind::Reference || TUK == TagUseKind::Friend || - isDeclInScope(DirectPrevDecl, SearchDC, S, - SS.isNotEmpty() || isMemberSpecialization)) { + isTagRedeclarationInScope(DirectPrevDecl, SearchDC, S, + SS.isNotEmpty() || + isMemberSpecialization)) { if (auto *RD = dyn_cast<CXXRecordDecl>(PrevDecl); RD && RD->isInjectedClassName()) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 4f7e59ea43ef1..b8b0c71894daa 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2110,7 +2110,8 @@ DeclResult Sema::CheckClassTemplate( PrevDecl = (*Previous.begin())->getUnderlyingDecl(); } } - } else if (PrevDecl && !isDeclInScope(Previous.getRepresentativeDecl(), + } else if (PrevDecl && + !isTagRedeclarationInScope(Previous.getRepresentativeDecl(), SemanticContext, S, SS.isValid())) PrevDecl = PrevClassTemplate = nullptr; diff --git a/clang/test/Modules/pr96423.cppm b/clang/test/Modules/pr96423.cppm new file mode 100644 index 0000000000000..a5dc3a484cfc0 --- /dev/null +++ b/clang/test/Modules/pr96423.cppm @@ -0,0 +1,41 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-module-interface -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t -verify -fsyntax-only +// +// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-reduced-module-interface -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t -verify -fsyntax-only + +//--- header.h +#ifndef HEADER_H +#define HEADER_H + +struct X; +template <class> struct Y; +void f(struct X *, Y<int> *); + +#endif + +//--- M.cppm +module; +#include "header.h" + +namespace N { +using ::X; +using ::Y; +} + +export module M; + +export namespace N { +using N::X; +using N::Y; +} + +//--- use.cpp +// expected-no-diagnostics +import M; +using namespace N; + +#include "header.h" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
