This patch adds support for the C11 keyword _Thread_local. Semantically it's essentially the same as __thread, except that it doesn't have the GNU C ordering restriction on __thread appearing before extern or static.
Bootstrapped with no regressions on x86_64-unknown-linux-gnu. Applied to mainline. c-family: 2013-11-12 Joseph Myers <jos...@codesourcery.com> * c-common.c (c_common_reswords): Add _Thread_local. c: 2013-11-12 Joseph Myers <jos...@codesourcery.com> * c-tree.h (struct c_declspecs): Add thread_gnu_p field. * c-parser.c (c_parser_declspecs): Mention _Thread_local in comment. * c-decl.c (shadow_tag_warned, grokdeclarator): Mention __thread or _Thread_local as appropriate in diagnostics. (build_null_declspecs): Initialize ret->thread_gnu_p. (declspecs_add_scspec): Handle either __thread or _Thread_local for RID_THREAD. Diagnose _Thread_local for pre-C11 standards if pedantic. Do not disallow _Thread_local extern and _Thread_local static. testsuite: 2013-11-12 Joseph Myers <jos...@codesourcery.com> * gcc.dg/c90-thread-local-1.c, gcc.dg/c99-thread-local-1.c, gcc.dg/c11-thread-local-1.c, gcc.dg/c11-thread-local-2.c: New tests. * gcc.dg/tls/diag-2.c, objc.dg/tls/diag-2.m: Update expected diagnostics. Index: gcc/c-family/c-common.c =================================================================== --- gcc/c-family/c-common.c (revision 204697) +++ gcc/c-family/c-common.c (working copy) @@ -424,6 +424,7 @@ const struct c_common_resword c_common_reswords[] { "_Static_assert", RID_STATIC_ASSERT, D_CONLY }, { "_Noreturn", RID_NORETURN, D_CONLY }, { "_Generic", RID_GENERIC, D_CONLY }, + { "_Thread_local", RID_THREAD, D_CONLY }, { "__FUNCTION__", RID_FUNCTION_NAME, 0 }, { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 }, { "__alignof", RID_ALIGNOF, 0 }, Index: gcc/testsuite/objc.dg/tls/diag-2.m =================================================================== --- gcc/testsuite/objc.dg/tls/diag-2.m (revision 204697) +++ gcc/testsuite/objc.dg/tls/diag-2.m (working copy) @@ -3,7 +3,7 @@ __thread extern int g1; /* { dg-error "'__thread' before 'extern'" } */ __thread static int g2; /* { dg-error "'__thread' before 'static'" } */ -__thread __thread int g3; /* { dg-error "duplicate '__thread'" } */ +__thread __thread int g3; /* { dg-error "duplicate" } */ typedef __thread int g4; /* { dg-error " '__thread' used with 'typedef'" } */ void foo() Index: gcc/testsuite/gcc.dg/c90-thread-local-1.c =================================================================== --- gcc/testsuite/gcc.dg/c90-thread-local-1.c (revision 0) +++ gcc/testsuite/gcc.dg/c90-thread-local-1.c (revision 0) @@ -0,0 +1,5 @@ +/* Test for _Thread_local: not in C90. */ +/* { dg-do compile } */ +/* { dg-options "-std=c90 -pedantic-errors" } */ + +static _Thread_local int x; /* { dg-error "_Thread_local" } */ Index: gcc/testsuite/gcc.dg/c11-thread-local-1.c =================================================================== --- gcc/testsuite/gcc.dg/c11-thread-local-1.c (revision 0) +++ gcc/testsuite/gcc.dg/c11-thread-local-1.c (revision 0) @@ -0,0 +1,28 @@ +/* Test for _Thread_local in C11. Test of valid code. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +_Thread_local int a; +static _Thread_local long b; +extern _Thread_local int c, a; +_Thread_local static int d; +long _Thread_local extern b; +_Thread_local int extern a; +_Thread_local struct s; /* { dg-warning "useless" } */ +_Thread_local int a = 1; +extern _Thread_local int c = 2; /* { dg-warning "initialized and" } */ +void +f (void) +{ + static _Thread_local int x; + extern _Thread_local long b; + _Thread_local extern int a; +} + +inline void +fi (void) +{ + static _Thread_local const int v; + (void) a; + static _Thread_local int (*const p)[a]; +} Index: gcc/testsuite/gcc.dg/tls/diag-2.c =================================================================== --- gcc/testsuite/gcc.dg/tls/diag-2.c (revision 204697) +++ gcc/testsuite/gcc.dg/tls/diag-2.c (working copy) @@ -3,7 +3,7 @@ __thread extern int g1; /* { dg-error "'__thread' before 'extern'" } */ __thread static int g2; /* { dg-error "'__thread' before 'static'" } */ -__thread __thread int g3; /* { dg-error "duplicate '__thread'" } */ +__thread __thread int g3; /* { dg-error "duplicate" } */ typedef __thread int g4; /* { dg-error "'__thread' used with 'typedef'" } */ void foo() Index: gcc/testsuite/gcc.dg/c99-thread-local-1.c =================================================================== --- gcc/testsuite/gcc.dg/c99-thread-local-1.c (revision 0) +++ gcc/testsuite/gcc.dg/c99-thread-local-1.c (revision 0) @@ -0,0 +1,5 @@ +/* Test for _Thread_local: not in C99. */ +/* { dg-do compile } */ +/* { dg-options "-std=c99 -pedantic-errors" } */ + +static _Thread_local int x; /* { dg-error "_Thread_local" } */ Index: gcc/testsuite/gcc.dg/c11-thread-local-2.c =================================================================== --- gcc/testsuite/gcc.dg/c11-thread-local-2.c (revision 0) +++ gcc/testsuite/gcc.dg/c11-thread-local-2.c (revision 0) @@ -0,0 +1,46 @@ +/* Test for _Thread_local in C11. Test of invalid code. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +_Thread_local void f (void); /* { dg-error "storage class" } */ +_Thread_local void g (void) {} /* { dg-error "_Thread_local" } */ +typedef _Thread_local int t1; /* { dg-error "_Thread_local" } */ +_Thread_local typedef int t2; /* { dg-error "_Thread_local" } */ + +void +h (void) +{ + _Thread_local auto int a; /* { dg-error "_Thread_local" } */ + _Thread_local register int b; /* { dg-error "_Thread_local" } */ + auto _Thread_local int c; /* { dg-error "_Thread_local" } */ + register _Thread_local int d; /* { dg-error "_Thread_local" } */ + _Thread_local int e; /* { dg-error "_Thread_local" } */ +} + +_Thread_local int v; /* { dg-message "previous" } */ +extern int v; /* { dg-error "thread" } */ +int w; /* { dg-message "previous" } */ +extern _Thread_local int w; /* { dg-error "thread" } */ + +_Thread_local int x; /* { dg-message "previous" } */ +int y; /* { dg-message "previous" } */ + +int vv; + +void +i (void) +{ + extern int x; /* { dg-error "thread" } */ + extern _Thread_local int y; /* { dg-error "thread" } */ + static _Thread_local int a[vv]; /* { dg-error "storage size" } */ + static _Thread_local int vi = vv; /* { dg-error "not constant" } */ +} + +static _Thread_local int sv; + +inline void +j (void) +{ + static _Thread_local int vj; /* { dg-error "static but declared" } */ + (void) sv; /* { dg-error "static but used in inline" } */ +} Index: gcc/c/c-parser.c =================================================================== --- gcc/c/c-parser.c (revision 204697) +++ gcc/c/c-parser.c (working copy) @@ -1969,7 +1969,10 @@ c_parser_static_assert_declaration_no_semi (c_pars static auto register + _Thread_local + (_Thread_local is new in C11.) + C99 6.7.4: function-specifier: inline Index: gcc/c/c-tree.h =================================================================== --- gcc/c/c-tree.h (revision 204697) +++ gcc/c/c-tree.h (working copy) @@ -320,8 +320,10 @@ struct c_declspecs { BOOL_BITFIELD inline_p : 1; /* Whether "_Noreturn" was speciied. */ BOOL_BITFIELD noreturn_p : 1; - /* Whether "__thread" was specified. */ + /* Whether "__thread" or "_Thread_local" was specified. */ BOOL_BITFIELD thread_p : 1; + /* Whether "__thread" rather than "_Thread_local" was specified. */ + BOOL_BITFIELD thread_gnu_p : 1; /* Whether "const" was specified. */ BOOL_BITFIELD const_p : 1; /* Whether "volatile" was specified. */ Index: gcc/c/c-decl.c =================================================================== --- gcc/c/c-decl.c (revision 204697) +++ gcc/c/c-decl.c (working copy) @@ -3805,7 +3805,8 @@ shadow_tag_warned (const struct c_declspecs *decls if (!warned && !in_system_header && declspecs->thread_p) { - warning (0, "useless %<__thread%> in empty declaration"); + warning (0, "useless %qs in empty declaration", + declspecs->thread_gnu_p ? "__thread" : "_Thread_local"); warned = 2; } @@ -5164,7 +5165,8 @@ grokdeclarator (const struct c_declarator *declara if (storage_class == csc_typedef) error_at (loc, "function definition declared %<typedef%>"); if (threadp) - error_at (loc, "function definition declared %<__thread%>"); + error_at (loc, "function definition declared %qs", + declspecs->thread_gnu_p ? "__thread" : "_Thread_local"); threadp = false; if (storage_class == csc_auto || storage_class == csc_register @@ -5233,8 +5235,8 @@ grokdeclarator (const struct c_declarator *declara else if (threadp && storage_class == csc_none) { error_at (loc, "function-scope %qE implicitly auto and declared " - "%<__thread%>", - name); + "%qs", name, + declspecs->thread_gnu_p ? "__thread" : "_Thread_local"); threadp = false; } } @@ -8980,6 +8982,7 @@ build_null_declspecs (void) ret->inline_p = false; ret->noreturn_p = false; ret->thread_p = false; + ret->thread_gnu_p = false; ret->const_p = false; ret->volatile_p = false; ret->atomic_p = false; @@ -9773,14 +9776,29 @@ declspecs_add_scspec (source_location loc, case RID_THREAD: dupe = specs->thread_p; if (specs->storage_class == csc_auto) - error ("%<__thread%> used with %<auto%>"); + error ("%qE used with %<auto%>", scspec); else if (specs->storage_class == csc_register) - error ("%<__thread%> used with %<register%>"); + error ("%qE used with %<register%>", scspec); else if (specs->storage_class == csc_typedef) - error ("%<__thread%> used with %<typedef%>"); + error ("%qE used with %<typedef%>", scspec); else { specs->thread_p = true; + specs->thread_gnu_p = (strcmp (IDENTIFIER_POINTER (scspec), + "__thread") == 0); + /* A diagnostic is not required for the use of this + identifier in the implementation namespace; only diagnose + it for the C11 spelling because of existing code using + the other spelling. */ + if (!flag_isoc11 && !specs->thread_gnu_p) + { + if (flag_isoc99) + pedwarn (loc, OPT_Wpedantic, + "ISO C99 does not support %qE", scspec); + else + pedwarn (loc, OPT_Wpedantic, + "ISO C90 does not support %qE", scspec); + } specs->locations[cdw_thread] = loc; } break; @@ -9790,7 +9808,7 @@ declspecs_add_scspec (source_location loc, case RID_EXTERN: n = csc_extern; /* Diagnose "__thread extern". */ - if (specs->thread_p) + if (specs->thread_p && specs->thread_gnu_p) error ("%<__thread%> before %<extern%>"); break; case RID_REGISTER: @@ -9799,7 +9817,7 @@ declspecs_add_scspec (source_location loc, case RID_STATIC: n = csc_static; /* Diagnose "__thread static". */ - if (specs->thread_p) + if (specs->thread_p && specs->thread_gnu_p) error ("%<__thread%> before %<static%>"); break; case RID_TYPEDEF: @@ -9811,7 +9829,12 @@ declspecs_add_scspec (source_location loc, if (n != csc_none && n == specs->storage_class) dupe = true; if (dupe) - error ("duplicate %qE", scspec); + { + if (i == RID_THREAD) + error ("duplicate %<_Thread_local%> or %<__thread%>"); + else + error ("duplicate %qE", scspec); + } if (n != csc_none) { if (specs->storage_class != csc_none && n != specs->storage_class) @@ -9824,7 +9847,9 @@ declspecs_add_scspec (source_location loc, specs->locations[cdw_storage_class] = loc; if (n != csc_extern && n != csc_static && specs->thread_p) { - error ("%<__thread%> used with %qE", scspec); + error ("%qs used with %qE", + specs->thread_gnu_p ? "__thread" : "_Thread_local", + scspec); specs->thread_p = false; } } -- Joseph S. Myers jos...@codesourcery.com