]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c: Do not allow thread-local tentative definitions for C2x
authorJoseph Myers <joseph@codesourcery.com>
Fri, 19 May 2023 00:42:07 +0000 (00:42 +0000)
committerJoseph Myers <joseph@codesourcery.com>
Fri, 19 May 2023 00:42:07 +0000 (00:42 +0000)
C2x makes it clear that thread-local declarations can never be
tentative definitions (the legacy feature of C where you can e.g. do
"int i;" more than once at file scope, possibly with one of the
declarations initialized, and it counts as exactly one definition),
but are always definitions in the absence of "extern".  The wording
about external definitions was unclear in the thread-local case in C11
/ C17 (both about what counts as a tentative definition, and what is a
"definition" at all), not having been updated to cover the addition of
thread-local storage.

Implement this C2x requirement.  Arguably this is a defect fix that
would be appropriate to apply for all standard versions, but for now
the change is conditional on flag_isoc2x (however, it doesn't handle
_Thread_local / thread_local any different from GNU __thread).  Making
the change unconditional results in various TLS tests failing to
compile (gcc.dg/c11-thread-local-1.c gcc.dg/tls/thr-init-1.c
gcc.dg/tls/thr-init-2.c gcc.dg/torture/tls/thr-init-2.c
objc.dg/torture/tls/thr-init.m), though it's not clear if those tests
reflect any real code similarly trying to make use of thread-local
tentative definitions.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.

gcc/c/
* c-decl.cc (diagnose_mismatched_decls): Do not handle
thread-local declarations as tentative definitions for C2x.
(finish_decl): Do not allow thread-local definition with
incomplete type for C2x.

gcc/testsuite/
* gcc.dg/c2x-thread-local-2.c: New test.

gcc/c/c-decl.cc
gcc/testsuite/gcc.dg/c2x-thread-local-2.c [new file with mode: 0644]

index 945e45bff89ca9bc163d411f6b5da91e240af814..b5b491cf2dac2be2979f8edbe3314d15f5f57617 100644 (file)
@@ -2442,8 +2442,20 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
          return false;
        }
 
-      /* Multiple initialized definitions are not allowed (6.9p3,5).  */
-      if (DECL_INITIAL (newdecl) && DECL_INITIAL (olddecl))
+      /* Multiple initialized definitions are not allowed (6.9p3,5).
+        For this purpose, C2x makes it clear that thread-local
+        declarations without extern are definitions, not tentative
+        definitions, whether or not they have initializers.  The
+        wording before C2x was unclear; literally it would have made
+        uninitialized thread-local declarations into tentative
+        definitions only if they also used static, but without saying
+        explicitly whether or not other cases count as
+        definitions at all.  */
+      if ((DECL_INITIAL (newdecl) && DECL_INITIAL (olddecl))
+         || (flag_isoc2x
+             && DECL_THREAD_LOCAL_P (newdecl)
+             && !DECL_EXTERNAL (newdecl)
+             && !DECL_EXTERNAL (olddecl)))
        {
          auto_diagnostic_group d;
          error ("redefinition of %q+D", newdecl);
@@ -5714,10 +5726,12 @@ finish_decl (tree decl, location_t init_loc, tree init,
              /* A static variable with an incomplete type
                 is an error if it is initialized.
                 Also if it is not file scope.
+                Also if it is thread-local (in C2x).
                 Otherwise, let it through, but if it is not `extern'
                 then it may cause an error message later.  */
              ? (DECL_INITIAL (decl) != NULL_TREE
-                || !DECL_FILE_SCOPE_P (decl))
+                || !DECL_FILE_SCOPE_P (decl)
+                || (flag_isoc2x && DECL_THREAD_LOCAL_P (decl)))
              /* An automatic variable with an incomplete type
                 is an error.  */
              : !DECL_EXTERNAL (decl)))
diff --git a/gcc/testsuite/gcc.dg/c2x-thread-local-2.c b/gcc/testsuite/gcc.dg/c2x-thread-local-2.c
new file mode 100644 (file)
index 0000000..d199ff2
--- /dev/null
@@ -0,0 +1,40 @@
+/* Test that thread-local declarations are not considered tentative definitions
+   in C2x.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c2x -pedantic-errors" } */
+
+thread_local int a; /* { dg-message "previous" } */
+thread_local int a; /* { dg-error "redefinition" } */
+
+static thread_local int b; /* { dg-message "previous" } */
+static thread_local int b; /* { dg-error "redefinition" } */
+
+thread_local int c; /* { dg-message "previous" } */
+thread_local int c = 1; /* { dg-error "redefinition" } */
+
+static thread_local int d; /* { dg-message "previous" } */
+static thread_local int d = 1; /* { dg-error "redefinition" } */
+
+thread_local int e = 1; /* { dg-message "previous" } */
+thread_local int e; /* { dg-error "redefinition" } */
+
+static thread_local int f = 1; /* { dg-message "previous" } */
+static thread_local int f; /* { dg-error "redefinition" } */
+
+/* Not being a tentative definition means that incomplete arrays are an error
+   rather than defaulting to size 1.  */
+thread_local int g[]; /* { dg-error "storage size" } */
+static thread_local int h[]; /* { dg-error "array size missing" } */
+extern thread_local int i[];
+
+thread_local int j[] = { 0 };
+static thread_local int k[] = { 0 };
+
+thread_local int l;
+extern thread_local int l;
+
+extern thread_local int m;
+thread_local int m;
+
+static thread_local int n;
+extern thread_local int n;