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