https://github.com/akash-manna-sky created 
https://github.com/llvm/llvm-project/pull/233389

Fixes #223003

Instantiating a block-scope `extern` variable looks up the instantiation of the 
pattern's previous declaration so the two can be merged. When both branches of 
an `if constexpr` redeclare a namespace-scope variable, the previous 
declaration is the one in the discarded branch. It was never instantiated, so 
`findInstantiationOf` asserted.

Walk the pattern's redeclaration chain past local declarations that have no 
instantiation in the current scope, so the new declaration merges with the 
nearest one that exists, here the global. A previous declaration in another 
dependent context still falls back to lookup as before.

Assisted-by : LLM tools


>From 82f682e2cd071c27af302792c8647f20e0334dee Mon Sep 17 00:00:00 2001
From: Akash Manna <[email protected]>
Date: Mon, 19 Oct 2026 14:31:34 +0530
Subject: [PATCH] [Clang] Fix crash when a local extern's previous declaration
 is in a discarded if constexpr branch

Fixes #223003

Instantiating a block-scope `extern` variable looks up the instantiation of the 
pattern's previous declaration so the two can be merged. When both branches of 
an `if constexpr` redeclare a namespace-scope variable, the previous 
declaration is the one in the discarded branch. It was never instantiated, so 
`findInstantiationOf` asserted.

Walk the pattern's redeclaration chain past local declarations that have no 
instantiation in the current scope, so the new declaration merges with the 
nearest one that exists, here the global. A previous declaration in another 
dependent context still falls back to lookup as before.

LLM tools were used for this contribution.
---
 clang/docs/ReleaseNotes.md                    |   3 +
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  28 +++-
 .../CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp  | 141 ++++++++++++++++++
 clang/test/CodeGenCXX/cxx1z-constexpr-if.cpp  |  18 +++
 ...n-statements-local-extern-if-constexpr.cpp |   8 +-
 5 files changed, 187 insertions(+), 11 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3f0c48cf36644..c3779651dae1e 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -974,6 +974,9 @@ features cannot lower the translation-unit ABI level;
 - Fixed deduction of the template parameters appearing in the type of a
   constant template parameter of reference type. (#GH40328)
 
+- Fixed a crash when instantiating a local `extern` variable declaration whose
+  previous declaration was in a discarded branch of an `if constexpr`. 
(#GH223003)
+
 - Fixed an issue where an explicit specialization of a constexpr variable would
   result in a link error. (#GH219796)
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index b4881d0c6db5c..a059c1c20eb72 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6474,13 +6474,31 @@ void Sema::BuildVariableInstantiation(
       NewVar->isLocalExternDecl() ? RedeclarationKind::ForExternalRedeclaration
                                   : forRedeclarationInCurContext());
 
-  if (NewVar->isLocalExternDecl() && OldVar->getPreviousDecl() &&
-      (!OldVar->getPreviousDecl()->getDeclContext()->isDependentContext() ||
-       OldVar->getPreviousDecl()->getDeclContext()==OldVar->getDeclContext())) 
{
+  VarDecl *PatternPrev = nullptr;
+  if (NewVar->isLocalExternDecl()) {
+    for (VarDecl *Prev = OldVar->getPreviousDecl(); Prev;
+         Prev = Prev->getPreviousDecl()) {
+      DeclContext *PrevDC = Prev->getDeclContext();
+      if (!PrevDC->isDependentContext()) {
+        PatternPrev = Prev;
+        break;
+      }
+      if (PrevDC != OldVar->getDeclContext())
+        break;
+      if (CurrentInstantiationScope &&
+          CurrentInstantiationScope->getInstantiationOfIfExists(Prev)) {
+        PatternPrev = Prev;
+        break;
+      }
+      // Discarded 'if constexpr' branch: look further back.
+    }
+  }
+
+  if (PatternPrev) {
     // We have a previous declaration. Use that one, so we merge with the
     // right type.
-    if (NamedDecl *NewPrev = FindInstantiatedDecl(
-            NewVar->getLocation(), OldVar->getPreviousDecl(), TemplateArgs))
+    if (NamedDecl *NewPrev = FindInstantiatedDecl(NewVar->getLocation(),
+                                                  PatternPrev, TemplateArgs))
       Previous.addDecl(NewPrev);
   } else if (!isa<VarTemplateSpecializationDecl>(NewVar) &&
              OldVar->hasLinkage()) {
diff --git a/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp 
b/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp
index 05830de9891fe..bedf8140881b6 100644
--- a/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp
+++ b/clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp
@@ -256,4 +256,145 @@ namespace GH153884 {
   }
 }
 
+namespace GH223003 {
+  int foo8_decl;
+
+  template <bool x>
+  void foo8() {
+    if constexpr (x)
+      extern int foo8_decl;
+    else
+      extern int foo8_decl;
+  }
+  template void foo8<false>();
+  template void foo8<true>();
+
+  template <bool x>
+  void foo8_use() {
+    if constexpr (x) {
+      extern int foo8_decl;
+      foo8_decl = 1;
+    } else {
+      extern int foo8_decl;
+      foo8_decl = 2;
+    }
+  }
+  template void foo8_use<false>();
+  template void foo8_use<true>();
+
+  template <bool x>
+  void plain_if() {
+    if (x) {
+      extern int foo8_decl;
+      foo8_decl = 1;
+    } else {
+      extern int foo8_decl;
+      foo8_decl = 2;
+    }
+  }
+  template void plain_if<false>();
+
+  extern int linked;
+  template <bool x>
+  constexpr int *addr() {
+    if constexpr (x) {
+      extern int linked;
+      return &linked;
+    } else {
+      extern int linked;
+      return &linked;
+    }
+  }
+  static_assert(addr<false>() == &linked, "");
+  static_assert(addr<true>() == &linked, "");
+
+  int three;
+  template <int n>
+  void three_decls() {
+    if constexpr (n == 1)
+      extern int three;
+    else if constexpr (n == 2)
+      extern int three;
+    else
+      extern int three;
+  }
+  template void three_decls<3>();
+  template void three_decls<2>();
+  template void three_decls<1>();
+
+  int nest;
+  template <bool a, bool b>
+  void nested() {
+    if constexpr (a) {
+      if constexpr (b)
+        extern int nest;
+      else
+        extern int nest;
+    } else {
+      extern int nest;
+    }
+  }
+  template void nested<false, false>();
+  template void nested<true, false>();
+  template void nested<true, true>();
+
+  template <bool x>
+  void local_prev() {
+    extern int local_only;
+    if constexpr (x)
+      extern int local_only;
+    else
+      extern int local_only;
+    local_only = 1;
+  }
+  template void local_prev<false>();
+
+  int in_lambda;
+  template <bool x>
+  void lambda() {
+    [] {
+      if constexpr (x)
+        extern int in_lambda;
+      else
+        extern int in_lambda;
+    }();
+  }
+  template void lambda<false>();
+
+  int in_generic;
+  void generic() {
+    [](auto x) {
+      if constexpr (sizeof(x) == 1)
+        extern int in_generic;
+      else
+        extern int in_generic;
+    }(0);
+  }
+
+  void fn_decl();
+  template <bool x>
+  void local_fn() {
+    if constexpr (x)
+      extern void fn_decl();
+    else
+      extern void fn_decl();
+    fn_decl();
+  }
+  template void local_fn<false>();
+
+  int dep; // #dep
+  template <typename T>
+  void dependent() {
+    if constexpr (sizeof(T) == 1)
+      extern T dep;
+    else
+      extern T dep; // #dep-else
+  }
+  template void dependent<long>(); // #dep-long
+  // expected-error@#dep-else {{redeclaration of 'dep' with a different type: 
'long' vs 'int'}}
+  // expected-note@#dep-long {{in instantiation of function template 
specialization 'GH223003::dependent<long>' requested here}}
+  // expected-note@#dep {{previous definition is here}}
+  template void dependent<int>();
+}
+
 #endif
diff --git a/clang/test/CodeGenCXX/cxx1z-constexpr-if.cpp 
b/clang/test/CodeGenCXX/cxx1z-constexpr-if.cpp
index 5a11afb8dec40..05f3f24756773 100644
--- a/clang/test/CodeGenCXX/cxx1z-constexpr-if.cpp
+++ b/clang/test/CodeGenCXX/cxx1z-constexpr-if.cpp
@@ -34,6 +34,24 @@ foo: should_not_be_used();
 // CHECK: should_be_used_2
 // CHECK: should_be_used_3
 
+namespace GH223003 {
+  static int internal_var = 42;
+  template <bool x>
+  int read() {
+    if constexpr (x) {
+      extern int internal_var;
+      return internal_var;
+    } else {
+      extern int internal_var;
+      return internal_var;
+    }
+  }
+  int use() { return read<false>(); }
+}
+
+// CHECK-LABEL: define {{.*}} @_ZN8GH2230034readILb0EEEiv(
+// CHECK: load i32, ptr @_ZN8GH223003L12internal_varE
+
 namespace BlockThisCapture {
   void foo();
   struct S {
diff --git 
a/clang/test/SemaCXX/expansion-statements-local-extern-if-constexpr.cpp 
b/clang/test/SemaCXX/expansion-statements-local-extern-if-constexpr.cpp
index cd83856a36c3b..d70e795f1f6ed 100644
--- a/clang/test/SemaCXX/expansion-statements-local-extern-if-constexpr.cpp
+++ b/clang/test/SemaCXX/expansion-statements-local-extern-if-constexpr.cpp
@@ -1,8 +1,4 @@
 // RUN: %clang_cc1 %s -I %S/Inputs -std=c++2c -fsyntax-only -verify
-// XFAIL: *
-
-// FIXME: This test currently asserts due to a bug not related to expansion 
statements:
-// https://github.com/llvm/llvm-project/issues/223003. Reenable it once that 
bug is fixed.
 
 int foo8_decl; // #foo8_decl
 void foo8() {
@@ -11,9 +7,9 @@ void foo8() {
       extern int foo8_decl;
     } else {
       extern thread_local int foo8_decl; // #mismatched_foo8_decl
-      // expected-error@#mismatched_foo8_decl {{thread-local declaration of 
'foo8_decl' follows non-thread-local declaration}}
+      // expected-error@#mismatched_foo8_decl 2 {{thread-local declaration of 
'foo8_decl' follows non-thread-local declaration}}
       // expected-note@#foo8_instantiation {{in instantiation of expansion 
statement requested here}}
-      // expected-note@#foo8_decl {{previous definition is here}}
+      // expected-note@#foo8_decl 2 {{previous definition is here}}
     }
   }
 }

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

Reply via email to