[clang] [clang] fix redeclarations of the injected class name (PR #207301)
Matheus Izvekov via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 2 18:08:33 PDT 2026
https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/207301
The declaration used to represent an injected class name should never be part of any redeclaration chain.
This is a regression since Clang 22, and this will be backported, so no release notes.
Fixes #202320
>From 247a27257805528c5f7db43c277b8a7f97c725a1 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Thu, 2 Jul 2026 17:16:52 -0300
Subject: [PATCH] [clang] fix redeclarations of the injected class name
The declaration used to represent an injected class name should never
be part of any redeclaration chain.
This is a regression since Clang 22, and this will be backported, so no release notes.
Fixes #202320
---
clang/lib/Sema/SemaDecl.cpp | 25 +++++++++++--------
.../SemaCXX/injected-class-name-crash.cpp | 7 ++++++
2 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f6d1d7a7877c7..813aebf263f97 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18447,16 +18447,6 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
Previous.resolveKind();
}
}
- } else if (auto *RD = dyn_cast<CXXRecordDecl>(PrevDecl);
- TUK == TagUseKind::Reference && RD &&
- RD->isInjectedClassName()) {
- // If lookup found the injected class name, the previous declaration is
- // the class being injected into.
- PrevDecl = cast<TagDecl>(RD->getDeclContext());
- Previous.clear();
- Previous.addDecl(PrevDecl);
- Previous.resolveKind();
- IsInjectedClassName = true;
}
}
@@ -18488,6 +18478,18 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
if (TUK == TagUseKind::Reference || TUK == TagUseKind::Friend ||
isDeclInScope(DirectPrevDecl, SearchDC, S,
SS.isNotEmpty() || isMemberSpecialization)) {
+
+ if (auto *RD = dyn_cast<CXXRecordDecl>(PrevDecl);
+ RD && RD->isInjectedClassName()) {
+ // If lookup found the injected class name, the previous declaration
+ // is the class being injected into.
+ Previous.clear();
+ PrevDecl = PrevTagDecl = cast<CXXRecordDecl>(RD->getDeclContext());
+ Previous.addDecl(PrevDecl);
+ Previous.resolveKind();
+ IsInjectedClassName = true;
+ }
+
// Make sure that this wasn't declared as an enum and now used as a
// struct or something similar.
if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind,
@@ -18688,7 +18690,8 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
// Okay, we're going to make a redeclaration. If this is some kind
// of reference, make sure we build the redeclaration in the same DC
// as the original, and ignore the current access specifier.
- if (TUK == TagUseKind::Friend || TUK == TagUseKind::Reference) {
+ if (TUK == TagUseKind::Friend || TUK == TagUseKind::Reference ||
+ IsInjectedClassName) {
SearchDC = PrevTagDecl->getDeclContext();
AS = AS_none;
}
diff --git a/clang/test/SemaCXX/injected-class-name-crash.cpp b/clang/test/SemaCXX/injected-class-name-crash.cpp
index a044ba064b58e..141762fe41d93 100644
--- a/clang/test/SemaCXX/injected-class-name-crash.cpp
+++ b/clang/test/SemaCXX/injected-class-name-crash.cpp
@@ -9,3 +9,10 @@ struct X : public Foo<Bar { // expected-error {{unknown template name 'Foo'}} ex
template <class T>
X<T>::X() {
}
+
+namespace GH202320 {
+ struct S { S f(); };
+ struct S::S;
+ // expected-error at -1 {{forward declaration of struct cannot have a nested name specifier}}
+ S S::f() { return S(); }
+} // namespace GH202320
More information about the cfe-commits
mailing list