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
