https://github.com/ChuanqiXu9 created 
https://github.com/llvm/llvm-project/pull/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.

>From 76047d65062d16f000f78ddb92674309285ee858 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Fri, 4 Sep 2026 14:45:30 +0800
Subject: [PATCH] [C++20] [Modules] Identify the tag redecl through Using

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.
---
 clang/include/clang/Sema/Sema.h |  7 ++++++
 clang/lib/Sema/SemaDecl.cpp     | 16 +++++++++++--
 clang/lib/Sema/SemaTemplate.cpp |  3 ++-
 clang/test/Modules/pr96423.cppm | 41 +++++++++++++++++++++++++++++++++
 4 files changed, 64 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Modules/pr96423.cppm

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 dd17f9365a5a1..c1f1103824e6a 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

Reply via email to