Author: ahmed mohamed kamel Date: 2026-10-06T17:51:51Z New Revision: b8ff1bc057f3631286194a22a77155fc4a4b467f
URL: https://github.com/llvm/llvm-project/commit/b8ff1bc057f3631286194a22a77155fc4a4b467f DIFF: https://github.com/llvm/llvm-project/commit/b8ff1bc057f3631286194a22a77155fc4a4b467f.diff LOG: [clang] Fix thread_local incorrectly treated as tentative definition (#219780) Fixes #217636. In C23 (6.9.3p1-2), a file-scope declaration with `thread_local` is a definition and is excluded from tentative definitions. Clang classified such declarations as tentative, so `thread_local int x; thread_local int x;` was accepted. In C23 and later, `VarDecl::isThisDeclarationADefinition` no longer returns `TentativeDefinition` for file-scope thread-local variables, and the C path of `MergeVarDecl` now diagnoses a redefinition when a thread-local definition follows a previous definition. Earlier language modes keep the old behavior, since older standards have no such rule. Tests: `Sema/c23-thread-local-definition.c` covers both C23 and pre-C23. Added: clang/test/Sema/c23-thread-local-definition.c Modified: clang/docs/ReleaseNotes.md clang/lib/AST/Decl.cpp clang/lib/Sema/SemaDecl.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index aca8d5bf630b2..c679846ee1e86 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -609,6 +609,8 @@ features cannot lower the translation-unit ABI level; - No longer crashing due to follow-on diagnostics when there is an invalid operand in a logical operator involving a vector operand. (#GH227588) - Fixed assertion failures caused by stale linkage information when an extern variable or function declaration is merged with a preceding static declaration. (#GH204759, #GH204754) - Fixed a crash due to typo correction mishandling custom keywords `_virtual_inheritance` and `_multiple_inheritance` in `-fms-compatibility` mode. (#GH228003) +- Clang no longer treats a file-scope `thread_local` declaration without an initializer as a tentative definition in C23 mode. As specified by C23 6.9.3, such a declaration is a definition, so declaring the same variable more that once is now diagnosed as a redefinition. (#GH217636) + #### Bug Fixes to Compiler Builtins - Fixed a crash when classifying a call to a builtin with dependent arguments, diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index f7a635e35336a..0c79fe07f8d0c 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2309,16 +2309,21 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const { if (isSingleLineLanguageLinkage(*this)) return DeclarationOnly; - // C99 6.9.2p2: - // A declaration of an object that has file scope without an initializer, - // and without a storage class specifier or the scs 'static', constitutes - // a tentative definition. - // No such thing in C++. - if (!C.getLangOpts().CPlusPlus && isFileVarDecl()) + // C23 6.9.3p2: + // A declaration of an identifier for an object that has file scope + // without an initializer, and without the storage-class specifier extern + // or thread_local, constitutes a tentative definition. + // In C23 and later, file-scope thread_local / _Thread_local / __thread + // declarations without initializers are full external definitions + // (C23 6.9.3p1). Pre-C23 standards allowed tentative TLS definitions, so we + // preserve that behavior in earlier language modes. + if (!C.getLangOpts().CPlusPlus && isFileVarDecl() && + (getTLSKind() == TLS_None || !C.getLangOpts().C23)) return TentativeDefinition; - // What's left is (in C, block-scope) declarations without initializers or - // external storage. These are definitions. + // What's left is (in C) block-scope declarations and, in C23, file-scope + // thread_local declarations without initializers or external storage. These + // are definitions. return Definition; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 59c0faa67364a..fdbe152116512 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4976,7 +4976,13 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) { // [basic.def]p2 for details, but the basic idea is: if the old declaration // contains the extern specifier and doesn't have an initializer, it's fine // in C++. - if (Old->getStorageClass() != SC_Extern || Old->hasInit()) { + if (New->getTLSKind() != VarDecl::TLS_None && + New->isThisDeclarationADefinition() == VarDecl::Definition) { + VarDecl *Def = Old->getDefinition(); + if (Def && checkVarDeclRedefinition(Def, New)) { + return; + } + } else if (Old->getStorageClass() != SC_Extern || Old->hasInit()) { Diag(New->getLocation(), diag::warn_cxx_compat_tentative_definition) << New; Diag(Old->getLocation(), diag::note_previous_declaration); diff --git a/clang/test/Sema/c23-thread-local-definition.c b/clang/test/Sema/c23-thread-local-definition.c new file mode 100644 index 0000000000000..cb5aeea2feb40 --- /dev/null +++ b/clang/test/Sema/c23-thread-local-definition.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c17 -fsyntax-only -verify=pre23 %s +// pre23-no-diagnostics + +_Thread_local int a; // expected-note {{previous definition is here}} +_Thread_local int a; // expected-error {{redefinition of 'a'}} + +static _Thread_local int b; // expected-note {{previous definition is here}} +static _Thread_local int b; // expected-error {{redefinition of 'b'}} + +__thread int c; // expected-note {{previous definition is here}} +__thread int c; // expected-error {{redefinition of 'c'}} + +// 'extern' makes these declarations, not definitions. +extern _Thread_local int e; +_Thread_local int e; + +_Thread_local int f; +extern _Thread_local int f; + +#if __STDC_VERSION__ >= 202311L +thread_local int x, x; // expected-error {{redefinition of 'x'}} \ + // expected-note {{previous definition is here}} +#endif _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
