lint: in C23 mode, recognize the keyword 'thread_local'

This commit is contained in:
rillig 2023-07-13 20:30:21 +00:00
parent 98b184405d
commit 78e3dd62e3
4 changed files with 53 additions and 18 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: c11.c,v 1.2 2023/07/12 18:26:04 rillig Exp $ */
/* $NetBSD: c11.c,v 1.3 2023/07/13 20:30:21 rillig Exp $ */
# 3 "c11.c"
/*
@ -42,6 +42,12 @@ three_times(void)
exit(0);
}
// In C11 mode, 'thread_local' is not yet known, but '_Thread_local' is.
/* expect+2: error: old-style declaration; add 'int' [1] */
/* expect+1: error: syntax error 'int' [249] */
thread_local int thread_local_variable_c23;
_Thread_local int thread_local_variable_c11;
/* The '_Noreturn' must not appear after the declarator. */
void _Noreturn exit(int) _Noreturn;
/* expect-1: error: formal parameter #1 lacks name [59] */

View File

@ -1,4 +1,4 @@
/* $NetBSD: c23.c,v 1.1 2023/07/02 23:45:10 rillig Exp $ */
/* $NetBSD: c23.c,v 1.2 2023/07/13 20:30:21 rillig Exp $ */
# 3 "c23.c"
// Tests for the option -Ac23, which allows features from C23 and all earlier
@ -16,7 +16,27 @@ c23(void)
int member;
} s;
// Empty initializer braces were introduced in C23.
s = (struct s){};
s = (struct s){s.member};
return s.member;
}
// The keyword 'thread_local' was introduced in C23.
thread_local int globally_visible;
// Thread-local functions don't make sense; they are syntactically allowed,
// though.
thread_local void
thread_local_function(void)
{
}
void
function(void)
{
// Not sure whether it makes sense to have a function-scoped
// thread-local variable. Don't warn for now, let the compilers handle
// this case.
thread_local int function_scoped_thread_local;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: decl.c,v 1.352 2023/07/13 19:59:08 rillig Exp $ */
/* $NetBSD: decl.c,v 1.353 2023/07/13 20:30:21 rillig Exp $ */
/*
* Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved.
@ -38,7 +38,7 @@
#include <sys/cdefs.h>
#if defined(__RCSID)
__RCSID("$NetBSD: decl.c,v 1.352 2023/07/13 19:59:08 rillig Exp $");
__RCSID("$NetBSD: decl.c,v 1.353 2023/07/13 20:30:21 rillig Exp $");
#endif
#include <sys/param.h>
@ -1514,7 +1514,8 @@ declarator_name(sym_t *sym)
*/
sc = AUTO;
sym->s_def = DEF;
} else if (sc == AUTO || sc == STATIC || sc == TYPEDEF)
} else if (sc == AUTO || sc == STATIC || sc == TYPEDEF
|| sc == THREAD_LOCAL)
sym->s_def = DEF;
else if (sc == REG) {
sym->s_register = true;

View File

@ -1,4 +1,4 @@
/* $NetBSD: lex.c,v 1.183 2023/07/13 19:59:08 rillig Exp $ */
/* $NetBSD: lex.c,v 1.184 2023/07/13 20:30:21 rillig Exp $ */
/*
* Copyright (c) 1996 Christopher G. Demetriou. All Rights Reserved.
@ -38,7 +38,7 @@
#include <sys/cdefs.h>
#if defined(__RCSID)
__RCSID("$NetBSD: lex.c,v 1.183 2023/07/13 19:59:08 rillig Exp $");
__RCSID("$NetBSD: lex.c,v 1.184 2023/07/13 20:30:21 rillig Exp $");
#endif
#include <ctype.h>
@ -68,18 +68,22 @@ bool in_gcc_attribute;
bool in_system_header;
/*
* Valid values for 'since' are 78, 90, 99, 11.
* Valid values for 'since' are 78, 90, 99, 11, 23.
*
* The C11 keywords are added in C99 mode as well, to provide good error
* messages instead of a simple parse error. If the keyword '_Generic' were
* not defined, it would be interpreted as an implicit function call, leading
* to a parse error.
* The C11 keywords are all taken from the reserved namespace. They are added
* in C99 mode as well, to make the parse error messages more useful. For
* example, if the keyword '_Generic' were not defined, it would be interpreted
* as an implicit function call, leading to a parse error.
*
* The C23 keywords are not made available in earlier modes, as they may
* conflict with user-defined identifiers.
*/
#define kwdef(name, token, detail, since, gcc, deco) \
{ \
{ /* CONSTCOND */ \
name, token, detail, \
(since) == 90, \
/* CONSTCOND */ (since) == 99 || (since) == 11, \
(since) == 99 || (since) == 11, \
(since) == 23, \
(gcc) > 0, \
((deco) & 1) != 0, ((deco) & 2) != 0, ((deco) & 4) != 0, \
}
@ -107,8 +111,9 @@ static const struct keyword {
function_specifier kw_fs; /* if kw_token is
* T_FUNCTION_SPECIFIER */
} u;
bool kw_c90:1; /* available in C90 mode */
bool kw_c99_or_c11:1; /* available in C99 or C11 mode */
bool kw_added_in_c90:1;
bool kw_added_in_c99_or_c11:1;
bool kw_added_in_c23:1;
bool kw_gcc:1; /* available in GCC mode */
bool kw_plain:1; /* 'name' */
bool kw_leading:1; /* '__name' */
@ -164,6 +169,7 @@ static const struct keyword {
kwdef_token( "__symbolrename", T_SYMBOLRENAME, 78,0,1),
kwdef_sclass( "__thread", THREAD_LOCAL, 78,1,1),
kwdef_sclass( "_Thread_local", THREAD_LOCAL, 11,0,1),
kwdef_sclass( "thread_local", THREAD_LOCAL, 23,0,1),
kwdef_sclass( "typedef", TYPEDEF, 78,0,1),
kwdef_token( "typeof", T_TYPEOF, 78,1,7),
#ifdef INT128_SIZE
@ -364,7 +370,9 @@ static bool
is_keyword_known(const struct keyword *kw)
{
if ((kw->kw_c90 || kw->kw_c99_or_c11) && !allow_c90)
if (kw->kw_added_in_c23 && !allow_c23)
return false;
if ((kw->kw_added_in_c90 || kw->kw_added_in_c99_or_c11) && !allow_c90)
return false;
/*
@ -378,7 +386,7 @@ is_keyword_known(const struct keyword *kw)
if (kw->kw_gcc)
return false;
if (kw->kw_c99_or_c11 && !allow_c99)
if (kw->kw_added_in_c99_or_c11 && !allow_c99)
return false;
return true;
}