https://github.com/ChenMiaoi created 
https://github.com/llvm/llvm-project/pull/236758

When a thread storage specifier conflicts with another storage class, DeclSpec 
clears it before diagnosing a C23 constexpr conflict. The second diagnostic 
then reports "unspecified" and loses the specifier's source range.

For a block-scope declaration:

Before:
```
a:2:22: error: cannot combine with previous 'register' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |             ~~~~~~~~ ^
a:2:3: error: cannot combine with previous 'unspecified' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |   ^
2 errors generated.
```

After:
```
a:2:22: error: cannot combine with previous 'register' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |             ~~~~~~~~ ^
a:2:3: error: cannot combine with previous '_Thread_local' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |   ^                  ~~~~~~~~~~~~~
2 errors generated.
```

Delay clearing the thread storage specifier until after the constexpr 
diagnostic.

>From fd650190bfdbd3d65aca6f62e440901cd3b8c010 Mon Sep 17 00:00:00 2001
From: Chen Miao <[email protected]>
Date: Thu, 29 Oct 2026 21:04:36 +0800
Subject: [PATCH] [clang][C23] Delay thread storage recovery for constexpr
 diagnostics

When a thread storage specifier conflicts with another storage class,
DeclSpec clears it before diagnosing a C23 constexpr conflict. The second
diagnostic then reports "unspecified" and loses the specifier's source range.

For a block-scope declaration:

Before:
```
a:2:22: error: cannot combine with previous 'register' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |             ~~~~~~~~ ^
a:2:3: error: cannot combine with previous 'unspecified' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |   ^
2 errors generated.
```

After:
```
a:2:22: error: cannot combine with previous 'register' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |             ~~~~~~~~ ^
a:2:3: error: cannot combine with previous '_Thread_local' declaration specifier
    2 |   constexpr register _Thread_local int x = 1;
      |   ^                  ~~~~~~~~~~~~~
2 errors generated.
```

Delay clearing the thread storage specifier until after the constexpr
diagnostic.
---
 clang/lib/Sema/DeclSpec.cpp | 12 +++++++++---
 clang/test/Sema/constexpr.c |  5 +++++
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index dfa7a79d1f92b2..acd649b89fb540 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1380,6 +1380,8 @@ void DeclSpec::CheckTypeSpec(Sema &S, const 
PrintingPolicy &Policy) {
   // _Thread_local can only appear with the 'static' and 'extern' storage class
   // specifiers. We also allow __private_extern__ as an extension.
   if (ThreadStorageClassSpec != TSCS_unspecified) {
+    bool DiscardThreadStorageClassSpec = false;
+
     switch (StorageClassSpec) {
     case SCS_unspecified:
     case SCS_extern:
@@ -1398,9 +1400,7 @@ void DeclSpec::CheckTypeSpec(Sema &S, const 
PrintingPolicy &Policy) {
              diag::err_invalid_decl_spec_combination)
           << DeclSpec::getSpecifierName(getStorageClassSpec())
           << SourceRange(getStorageClassSpecLoc());
-      // Discard the thread storage class specifier to recover.
-      ThreadStorageClassSpec = TSCS_unspecified;
-      ThreadStorageClassSpecLoc = SourceLocation();
+      DiscardThreadStorageClassSpec = true;
     }
     if (S.getLangOpts().C23 &&
         getConstexprSpecifier() == ConstexprSpecKind::Constexpr) {
@@ -1408,6 +1408,12 @@ void DeclSpec::CheckTypeSpec(Sema &S, const 
PrintingPolicy &Policy) {
           << DeclSpec::getSpecifierName(getThreadStorageClassSpec())
           << SourceRange(getThreadStorageClassSpecLoc());
     }
+    if (DiscardThreadStorageClassSpec) {
+      // Delay recovery until after diagnosing a possible constexpr conflict.
+      // FIXME: Clear C23ThreadLocalSpelling here when that field is added.
+      ThreadStorageClassSpec = TSCS_unspecified;
+      ThreadStorageClassSpecLoc = SourceLocation();
+    }
   }
 
   if (S.getLangOpts().C23 && getTypeSpecType() != DeclSpec::TST_unspecified &&
diff --git a/clang/test/Sema/constexpr.c b/clang/test/Sema/constexpr.c
index 3bb8cffe16ab21..aedaba2cb921a7 100644
--- a/clang/test/Sema/constexpr.c
+++ b/clang/test/Sema/constexpr.c
@@ -52,6 +52,11 @@ constexpr typedef int Bar; // expected-error {{typedef 
cannot be constexpr}}
 void f3(constexpr register int P1) { // expected-error {{function parameter 
cannot be constexpr}}
   constexpr register int V9 = 0;
   constexpr register auto V10 = 0.0;
+  // FIXME: The constexpr diagnostic calls a later _Thread_local "previous".
+  // Prefer "'_Thread_local' used with 'constexpr'".
+  constexpr register _Thread_local int V10a = 0;
+  // expected-error@-1 {{previous 'register' declaration specifier}}
+  // expected-error@-2 {{previous '_Thread_local' declaration specifier}}
 }
 
 constexpr thread_local int V11 = 38; // expected-error {{cannot combine with 
previous '_Thread_local' declaration specifier}}

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

Reply via email to