https://github.com/AhmedKamel10 updated https://github.com/llvm/llvm-project/pull/219780
>From 5eaf3f6e46cefc81bcb5bf60b0f48d0e784f16e0 Mon Sep 17 00:00:00 2001 From: ahmedkamel10 <[email protected]> Date: Sun, 30 Aug 2026 09:50:50 +0300 Subject: [PATCH] [Clang] Prevent thread_local from creating tentative definitions (#217636) --- clang/lib/AST/Decl.cpp | 18 +++++++++++------- clang/lib/Sema/SemaDecl.cpp | 8 +++++++- .../test/CodeGen/PowerPC/toc-data-attribute.c | 3 +-- .../CodeGen/PowerPC/toc-data-diagnostics.c | 2 +- clang/test/CodeGen/thread-specifier.c | 4 ++-- clang/test/Sema/PowerPC/aix-attr-tls_model.c | 8 ++++---- clang/test/Sema/attr-tls_model.c | 10 ++++++---- clang/test/Sema/tentative-decls.c | 3 +++ 8 files changed, 35 insertions(+), 21 deletions(-) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b145560508e95..afc53ff5cc3f2 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2309,16 +2309,20 @@ 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. + // 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. + // File-scope thread_local objects are instead external definitions (C23 + // 6.9.3p1). Earlier standards have no thread_local, but we apply the rule + // in every C mode, including for __thread. // No such thing in C++. - if (!C.getLangOpts().CPlusPlus && isFileVarDecl()) + if (!C.getLangOpts().CPlusPlus && isFileVarDecl() && getTLSKind() == TLS_None) 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 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 094601a58d508..dbf623a4874ac 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/CodeGen/PowerPC/toc-data-attribute.c b/clang/test/CodeGen/PowerPC/toc-data-attribute.c index d3a2d43b064ff..7c2459cb2588f 100644 --- a/clang/test/CodeGen/PowerPC/toc-data-attribute.c +++ b/clang/test/CodeGen/PowerPC/toc-data-attribute.c @@ -41,10 +41,9 @@ struct SomeStruct* getAddress(void) { // CHECK32: @l = global double 2.500000e+00, align 8 // CHECK64: @l = global double 2.500000e+00, align 8 #0 // COMMON: @m = global i32 10, section "foo", align 4 +// COMMON: @n = thread_local global i32 0, align 4 // COMMON: @f = external global i32, align 4 #0 // COMMON: @o = external global %struct.SomeStruct, align 1 // CHECK32: @i = global ptr null, align 4 #0 // CHECK64: @i = global ptr null, align 8 #0 -// COMMON: @n = thread_local global i32 0, align 4 // COMMON: @p = external global [0 x i32], align 4 -// COMMON: attributes #0 = { "toc-data" } diff --git a/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c b/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c index 169c355136993..77e9d399288c0 100644 --- a/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c +++ b/clang/test/CodeGen/PowerPC/toc-data-diagnostics.c @@ -56,10 +56,10 @@ long long test2() { // CHECK: @c = global i32 0, align 128 // CHECK: @d = global double 2.500000e+00, align 8 // CHECK: @e = global i32 10, section "foo", align 4 +// CHECK: @f = thread_local global i32 0, align 4 // CHECK: @globalOneWithAlias = global i32 10, align 4 // CHECK: @globalTwoWithAlias = global i32 20, align 4 // CHECK: @a = global %struct.large_struct zeroinitializer, align 4 -// CHECK: @f = thread_local global i32 0, align 4 // CHECK: @h = external global [0 x i32], align 4 // CHECK: @g = external global %struct.SomeStruct, align 1 // CHECK: @test2.ll = internal global i64 5, align 8 diff --git a/clang/test/CodeGen/thread-specifier.c b/clang/test/CodeGen/thread-specifier.c index 18a14f912aba4..ed8542cafa92d 100644 --- a/clang/test/CodeGen/thread-specifier.c +++ b/clang/test/CodeGen/thread-specifier.c @@ -2,15 +2,15 @@ // RUN: %clang_cc1 -triple riscv32 -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple riscv64 -emit-llvm -o - %s | FileCheck %s +// CHECK: @a ={{.*}} thread_local global // CHECK: @b = external thread_local global // CHECK: @d.e = internal thread_local global // CHECK: @d.f = internal thread_local global -// CHECK: @f.a = internal thread_local(initialexec) global -// CHECK: @a ={{.*}} thread_local global // CHECK: @g ={{.*}} thread_local global // CHECK: @h ={{.*}} thread_local(localdynamic) global // CHECK: @i ={{.*}} thread_local(initialexec) global // CHECK: @j ={{.*}} thread_local(localexec) global +// CHECK: @f.a = internal thread_local(initialexec) global // CHECK-NOT: @_ZTW // CHECK-NOT: @_ZTH diff --git a/clang/test/Sema/PowerPC/aix-attr-tls_model.c b/clang/test/Sema/PowerPC/aix-attr-tls_model.c index 7c2047bced939..616f5526932fd 100644 --- a/clang/test/Sema/PowerPC/aix-attr-tls_model.c +++ b/clang/test/Sema/PowerPC/aix-attr-tls_model.c @@ -5,7 +5,7 @@ #error "Should support tls_model attribute" #endif -static __thread int y __attribute((tls_model("global-dynamic"))); // no-warning -static __thread int y __attribute((tls_model("local-dynamic"))); // expected-no-diagnostics -static __thread int y __attribute((tls_model("initial-exec"))); // no-warning -static __thread int y __attribute((tls_model("local-exec"))); // no-warning +static __thread int y_gd __attribute((tls_model("global-dynamic"))); // no-warning +static __thread int y_ld __attribute((tls_model("local-dynamic"))); // expected-no-diagnostics +static __thread int y_ie __attribute((tls_model("initial-exec"))); // no-warning +static __thread int y_le __attribute((tls_model("local-exec"))); // no-warning \ No newline at end of file diff --git a/clang/test/Sema/attr-tls_model.c b/clang/test/Sema/attr-tls_model.c index ec99c7aacaa3a..dfc94e663ea13 100644 --- a/clang/test/Sema/attr-tls_model.c +++ b/clang/test/Sema/attr-tls_model.c @@ -7,8 +7,10 @@ int f(void) __attribute((tls_model("global-dynamic"))); // expected-error {{'tls_model' attribute only applies to thread-local variables}} int x __attribute((tls_model("global-dynamic"))); // expected-error {{'tls_model' attribute only applies to thread-local variables}} -static __thread int y __attribute((tls_model("global-dynamic"))); // no-warning +static __thread int y_ok __attribute((tls_model("global-dynamic"))); // no-warning + +static __thread int y_two_args __attribute((tls_model("local", "dynamic"))); // expected-error {{'tls_model' attribute takes one argument}} +static __thread int y_not_str __attribute((tls_model(123))); // expected-error {{expected string literal as argument of 'tls_model' attribute}} +static __thread int y_bad_model __attribute((tls_model("foobar"))); // expected-error {{tls_model must be "global-dynamic", "local-dynamic", "initial-exec" or "local-exec"}} + -static __thread int y __attribute((tls_model("local", "dynamic"))); // expected-error {{'tls_model' attribute takes one argument}} -static __thread int y __attribute((tls_model(123))); // expected-error {{expected string literal as argument of 'tls_model' attribute}} -static __thread int y __attribute((tls_model("foobar"))); // expected-error {{tls_model must be "global-dynamic", "local-dynamic", "initial-exec" or "local-exec"}} diff --git a/clang/test/Sema/tentative-decls.c b/clang/test/Sema/tentative-decls.c index 94d21bdbf94da..3d2dd147905cc 100644 --- a/clang/test/Sema/tentative-decls.c +++ b/clang/test/Sema/tentative-decls.c @@ -63,3 +63,6 @@ static int b0; static int a0[] = { 4 }; static int b0 = 5; + +_Thread_local int tls_redef; // expected-note {{previous definition is here}} +_Thread_local int tls_redef; // expected-error {{redefinition of 'tls_redef'}} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
