From: Bart Van Assche <bvanassche@acm.org>
To: linux-sparse@vger.kernel.org
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
Bart Van Assche <bvanassche@acm.org>
Subject: [PATCH 50/73] c23: Support constexpr object specifier
Date: Mon, 9 Nov 2026 12:32:36 -0800 [thread overview]
Message-ID: <20261109203259.362845-51-bvanassche@acm.org> (raw)
In-Reply-To: <20261109203259.362845-1-bvanassche@acm.org>
Add support for the ISO C23 'constexpr' object specifier and enforce its
linkage, qualification, and constant-initializer rules:
- Apply 'const' qualification after declarator parsing (cloning array
chains) so pointers become top-level const ('int *const') and array
elements become const without mutating shared array typedefs.
- Include MOD_CONSTEXPR in MOD_IGNORE for _Generic selection and give
file-scope constexpr objects internal linkage per C23 6.2.2p3.
- Allow block-scope 'constexpr register' and 'constexpr auto', while
rejecting constexpr on functions, parameters, struct/union members,
empty declarations, typedefs, extern, thread_local, file-scope
register, VLAs, and volatile, _Atomic, or restrict types.
- Mark constexpr symbols underspecified during initializer evaluation and
validate scalar and aggregate initializers for null pointers, integer
constant expressions, and exact target representability.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
evaluate.c | 261 ++++++++++++++++++++++++++-
expand.c | 90 +++++++--
expand.h | 3 +
ident-list.h | 1 +
parse.c | 108 ++++++++++-
show-parse.c | 51 +++---
symbol.c | 6 +-
symbol.h | 17 +-
validation/c23-constexpr-error.c | 92 ++++++++++
validation/c23-constexpr-self-init.c | 41 +++++
validation/c23-constexpr.c | 100 ++++++++++
11 files changed, 706 insertions(+), 64 deletions(-)
create mode 100644 validation/c23-constexpr-error.c
create mode 100644 validation/c23-constexpr-self-init.c
create mode 100644 validation/c23-constexpr.c
diff --git a/evaluate.c b/evaluate.c
index 1b1a72577d18..b7e352f55e6c 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -35,6 +35,7 @@
#include <limits.h>
#include "evaluate.h"
+#include "expand.h"
#include "simplify.h"
#include "lib.h"
#include "allocate.h"
@@ -95,6 +96,8 @@ static struct symbol *evaluate_symbol_expression(struct expression *expr)
}
examine_symbol_type(sym);
+ if (sym->ctype.modifiers & MOD_CONSTEXPR)
+ evaluate_symbol(sym);
base_type = get_base_type(sym);
if (!base_type) {
@@ -111,6 +114,14 @@ static struct symbol *evaluate_symbol_expression(struct expression *expr)
expr->op = '*';
expr->unop = addr;
expr->flags = CEF_NONE;
+ if (sym->ctype.modifiers & MOD_CONSTEXPR) {
+ if (is_float_type(base_type))
+ expr->flags = CEF_SET_FLOAT;
+ else if (is_int_type(base_type))
+ expr->flags = CEF_SET_ICE;
+ else
+ expr->flags = CEF_ADDR;
+ }
/* The type of a symbol is the symbol itself! */
expr->ctype = sym;
@@ -2388,6 +2399,14 @@ static struct symbol *evaluate_member_dereference(struct expression *expr)
expr->type = EXPR_PREOP;
expr->op = '*';
expr->unop = add;
+ if (mod & MOD_CONSTEXPR) {
+ if (is_float_type(ctype))
+ expr->flags = CEF_SET_FLOAT;
+ else if (is_int_type(ctype))
+ expr->flags = CEF_SET_ICE;
+ else
+ expr->flags = CEF_ADDR;
+ }
expr->ctype = member;
return member;
@@ -2922,7 +2941,7 @@ found:
else
v = &top->ident_expression;
- mods |= ctype->ctype.modifiers & MOD_STORAGE;
+ mods |= ctype->ctype.modifiers & (MOD_STORAGE | MOD_CONSTEXPR);
if (handle_initializer(v, 1, lclass, top->ctype, mods))
continue;
@@ -3015,6 +3034,161 @@ static struct expression *handle_scalar(struct expression *e, int nested)
* { "string", ...} - we need to preserve that string literal recognizable
* until we dig into the inner struct.
*/
+static void check_constexpr_scalar_init(struct expression *orig,
+ struct symbol *target)
+{
+ struct symbol *ttype, *stype;
+ int tclass = classify_type(target, &ttype);
+ int sclass = classify_type(orig->ctype, &stype);
+ struct expression tmp;
+
+ if (!valid_type(target) || !valid_type(orig->ctype))
+ return;
+
+ if ((tclass & TYPE_PTR) || is_nullptr_type(ttype)) {
+ if (!(orig->flags & (CEF_ACE | CEF_ADDR))) {
+ sparse_error(orig->pos,
+ "non-constant initializer for constexpr object");
+ return;
+ }
+ tmp = *orig;
+ if (!is_null_pointer_constant(&tmp) &&
+ !(((sclass & TYPE_PTR) || is_nullptr_type(stype)) && is_zero_constant(&tmp)))
+ sparse_error(orig->pos,
+ "constexpr pointer initializer is not null");
+ return;
+ }
+
+ if (is_int(tclass)) {
+ int sw, tw;
+ bool s_unsigned, t_unsigned, s_neg;
+
+ if (!(orig->flags & (CEF_ACE | CEF_ADDR))) {
+ sparse_error(orig->pos,
+ "non-constant initializer for constexpr object");
+ return;
+ }
+ if (!(orig->flags & CEF_ICE) || !is_int(sclass)) {
+ sparse_error(
+ orig->pos,
+ "constexpr integer initializer is not an integer constant expression");
+ return;
+ }
+ tmp = *orig;
+ expand_constexpr_initializer(&tmp);
+ if (tmp.type != EXPR_VALUE) {
+ sparse_error(orig->pos,
+ "non-constant initializer for constexpr object");
+ return;
+ }
+ sw = stype->bit_size;
+ s_unsigned = (stype->ctype.modifiers & MOD_UNSIGNED) != 0;
+ s_neg = !s_unsigned && sw > 0 && sw <= 64 &&
+ (tmp.value & sign_bit(sw)) != 0;
+ if (is_bool_type(ttype)) {
+ if (s_neg || (tmp.value != 0 && tmp.value != 1))
+ sparse_error(
+ orig->pos,
+ "constexpr initializer not representable in target type");
+ return;
+ }
+ tw = ttype->bit_size;
+ t_unsigned = (ttype->ctype.modifiers & MOD_UNSIGNED) != 0;
+ if (s_neg) {
+ long long sval = sw > 64 ? (long long)tmp.value :
+ bits_extend(tmp.value, sw, 1);
+ if (t_unsigned ||
+ (tw < 64 && sval < -(1LL << (tw - 1))))
+ sparse_error(
+ orig->pos,
+ "constexpr initializer not representable in target type");
+ } else {
+ unsigned long long uval =
+ sw > 64 ? tmp.value :
+ (unsigned long long)zero_extend(tmp.value, sw);
+ int max_bits = t_unsigned ? tw : (tw - 1);
+ if ((max_bits <= 0 && uval != 0) ||
+ (max_bits > 0 && max_bits < 64 &&
+ (uval >> max_bits) != 0))
+ sparse_error(
+ orig->pos,
+ "constexpr initializer not representable in target type");
+ }
+ return;
+ }
+
+ if (tclass & TYPE_FLOAT) {
+ if (!(orig->flags & CEF_ACE) || !(sclass & TYPE_NUM)) {
+ sparse_error(orig->pos,
+ "non-constant initializer for constexpr object");
+ return;
+ }
+ tmp = *orig;
+ expand_constexpr_initializer(&tmp);
+ if (sclass & TYPE_FLOAT) {
+ long double fv;
+ if (tmp.type != EXPR_FVALUE) {
+ sparse_error(
+ orig->pos,
+ "non-constant initializer for constexpr object");
+ return;
+ }
+ fv = tmp.fvalue;
+ if (stype->rank > ttype->rank && fv == fv) {
+ bool ok = ttype->rank < 0 ?
+ ((long double)(float)fv == fv) :
+ (ttype->rank == 0 ?
+ ((long double)(double)fv == fv) :
+ true);
+ if (!ok)
+ sparse_error(
+ orig->pos,
+ "constexpr initializer not representable in target type");
+ }
+ } else if (is_int(sclass)) {
+ int sw = stype->bit_size;
+ bool s_unsigned = (stype->ctype.modifiers & MOD_UNSIGNED) != 0;
+ bool s_neg = !s_unsigned && sw > 0 && sw <= 64 &&
+ (tmp.value & sign_bit(sw)) != 0;
+ if (tmp.type != EXPR_VALUE) {
+ sparse_error(
+ orig->pos,
+ "non-constant initializer for constexpr object");
+ return;
+ }
+ if (s_neg) {
+ long long sval = sw > 64 ? (long long)tmp.value :
+ bits_extend(tmp.value, sw, 1);
+ long double ld = ttype->rank < 0 ?
+ (long double)(float)sval :
+ (ttype->rank == 0 ?
+ (long double)(double)sval :
+ (long double)sval);
+ if (sval != LLONG_MIN &&
+ (ld <= (long double)LLONG_MIN ||
+ (long long)ld != sval))
+ sparse_error(
+ orig->pos,
+ "constexpr initializer not representable in target type");
+ } else {
+ unsigned long long uval =
+ sw > 64 ? tmp.value :
+ (unsigned long long)zero_extend(tmp.value, sw);
+ long double ld = ttype->rank < 0 ?
+ (long double)(float)uval :
+ (ttype->rank == 0 ?
+ (long double)(double)uval :
+ (long double)uval);
+ if (ld >= 18446744073709551616.0L ||
+ (unsigned long long)ld != uval)
+ sparse_error(
+ orig->pos,
+ "constexpr initializer not representable in target type");
+ }
+ }
+ }
+}
+
static int handle_initializer(struct expression **ep, int nested,
int class, struct symbol *ctype, unsigned long mods)
{
@@ -3026,20 +3200,27 @@ static int handle_initializer(struct expression **ep, int nested,
/* scalar */
if (!(class & TYPE_COMPOUND)) {
+ struct expression orig;
+
e = handle_scalar(e, nested);
+
if (!e)
return 0;
*ep = e;
if (!evaluate_expression(e))
return 1;
+ degenerate(e);
+ orig = *e;
compatible_assignment_types(e, ctype, ep, "initializer");
/*
* Initializers for static storage duration objects
* shall be constant expressions or a string literal [6.7.8(4)].
*/
mods |= ctype->ctype.modifiers;
- mods &= (MOD_TOPLEVEL | MOD_STATIC);
- if (mods && !(e->flags & (CEF_ACE | CEF_ADDR)))
+ if (mods & MOD_CONSTEXPR)
+ check_constexpr_scalar_init(&orig, ctype);
+ if ((mods & (MOD_TOPLEVEL | MOD_STATIC)) &&
+ !(e->flags & (CEF_ACE | CEF_ADDR)))
if (Wconstexpr_not_const)
warning(e->pos, "non-constant initializer for static object");
@@ -3084,12 +3265,18 @@ static int handle_initializer(struct expression **ep, int nested,
type = evaluate_expression(e);
if (!type)
return 0;
+ mods |= ctype->ctype.modifiers;
if (ctype->type == SYM_NODE)
ctype = ctype->ctype.base_type;
if (type->type == SYM_NODE)
type = type->ctype.base_type;
- if (ctype == type)
+ if (ctype == type) {
+ if ((mods & MOD_CONSTEXPR) && !(e->flags & (CEF_ACE | CEF_ADDR)))
+ sparse_error(
+ e->pos,
+ "non-constant initializer for constexpr object");
return 1;
+ }
return 0;
String:
@@ -3843,6 +4030,55 @@ void check_duplicates(struct symbol *sym)
}
}
+static bool check_constexpr_vm_type(struct symbol *type)
+{
+ while (type) {
+ examine_symbol_type(type);
+ switch (type->type) {
+ case SYM_NODE:
+ case SYM_PTR:
+ type = type->ctype.base_type;
+ continue;
+ case SYM_ARRAY:
+ if (type->array_size &&
+ type->array_size->type != EXPR_VALUE)
+ return true;
+ type = type->ctype.base_type;
+ continue;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+static bool has_invalid_constexpr_qualifiers(struct symbol *type)
+{
+ while (type) {
+ if (type->ctype.modifiers &
+ (MOD_VOLATILE | MOD_ATOMIC | MOD_RESTRICT))
+ return true;
+ switch (type->type) {
+ case SYM_NODE:
+ case SYM_ARRAY:
+ type = type->ctype.base_type;
+ continue;
+ case SYM_STRUCT:
+ case SYM_UNION: {
+ struct symbol *m;
+ FOR_EACH_PTR(type->symbol_list, m) {
+ if (has_invalid_constexpr_qualifiers(m))
+ return true;
+ } END_FOR_EACH_PTR(m);
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
static struct symbol *evaluate_symbol(struct symbol *sym)
{
struct symbol *base_type;
@@ -3858,9 +4094,24 @@ static struct symbol *evaluate_symbol(struct symbol *sym)
if (!base_type)
return NULL;
+ if ((sym->ctype.modifiers & MOD_CONSTEXPR) &&
+ base_type->type != SYM_FN && !(sym->ctype.modifiers & MOD_USERTYPE)) {
+ if (check_constexpr_vm_type(sym))
+ sparse_error(
+ sym->pos,
+ "constexpr variable '%s' cannot have variably modified type",
+ show_ident(sym->ident));
+ if (has_invalid_constexpr_qualifiers(sym))
+ sparse_error(
+ sym->pos,
+ "constexpr variable '%s' cannot have volatile, atomic, or restrict type",
+ show_ident(sym->ident));
+ }
+
/* Evaluate the initializers */
if (sym->initializer) {
- bool mark_underspec = sym->c23_auto;
+ bool mark_underspec = sym->c23_auto ||
+ (sym->ctype.modifiers & MOD_CONSTEXPR);
if (mark_underspec)
sym->underspecified = 1;
evaluate_initializer(sym, &sym->initializer);
diff --git a/expand.c b/expand.c
index 267aaf00e9ce..73b37b93534e 100644
--- a/expand.c
+++ b/expand.c
@@ -710,6 +710,41 @@ redo:
return &value;
}
+static struct expression *lookup_initializer_offset(struct expression *value, unsigned int offset, bool recurse)
+{
+ struct expression *entry;
+
+ FOR_EACH_PTR(value->expr_list, entry) {
+ if (entry->type == EXPR_INITIALIZER && recurse) {
+ struct expression *sub = lookup_initializer_offset(entry, offset, recurse);
+ if (sub)
+ return sub;
+ continue;
+ }
+ if (entry->type != EXPR_POS) {
+ if (offset)
+ continue;
+ return entry;
+ }
+ if (entry->init_nr > 1 && entry->ctype) {
+ int elem_bytes = bits_to_bytes(entry->ctype->bit_size);
+ if (elem_bytes > 0) {
+ unsigned long long end = (unsigned long long)entry->init_offset +
+ (unsigned long long)entry->init_nr * elem_bytes;
+ if (offset >= entry->init_offset && offset < end &&
+ ((offset - entry->init_offset) % elem_bytes) == 0)
+ return entry->init_expr;
+ }
+ }
+ if (entry->init_offset < offset)
+ continue;
+ if (entry->init_offset > offset)
+ break;
+ return entry->init_expr;
+ } END_FOR_EACH_PTR(entry);
+ return NULL;
+}
+
/*
* Look up a trustable initializer value at the requested offset.
*
@@ -719,35 +754,36 @@ static struct expression *constant_symbol_value(struct symbol *sym, int offset)
{
struct expression *value;
- if (offset < 0)
+ if (offset < 0 || sym->expanding)
return NULL;
if (sym->ctype.modifiers & MOD_VOLATILE)
return NULL;
- if (sym->ctype.modifiers & MOD_ACCESS)
+ if ((sym->ctype.modifiers & MOD_ACCESS) &&
+ !(sym->ctype.modifiers & MOD_CONSTEXPR))
return NULL;
if ((sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_STATIC)) &&
- !(sym->ctype.modifiers & MOD_CONST) &&
+ !(sym->ctype.modifiers & (MOD_CONST | MOD_CONSTEXPR)) &&
!sym->string && sym->ident)
return NULL;
value = sym->initializer;
if (!value)
return NULL;
+ sym->expanding = 1;
+ expand_expression(value);
+ sym->expanding = 0;
+ if (value->type == EXPR_PREOP && value->op == '*' &&
+ value->unop && value->unop->type == EXPR_SYMBOL &&
+ (value->unop->symbol->ctype.modifiers & MOD_CONSTEXPR)) {
+ sym->expanding = 1;
+ value = constant_symbol_value(value->unop->symbol, offset);
+ sym->expanding = 0;
+ return value;
+ }
if (value->type == EXPR_INITIALIZER) {
- struct expression *entry;
- FOR_EACH_PTR(value->expr_list, entry) {
- if (entry->type != EXPR_POS) {
- if (offset)
- continue;
- return entry;
- }
- if (entry->init_offset < offset)
- continue;
- if (entry->init_offset > offset)
- break;
- return entry->init_expr;
- } END_FOR_EACH_PTR(entry);
-
- value = default_initializer(sym, offset);
+ bool recurse = (sym->ctype.modifiers & (MOD_CONST | MOD_CONSTEXPR)) != 0;
+ value = lookup_initializer_offset(value, offset, recurse);
+ if (!value)
+ value = default_initializer(sym, offset);
}
return value;
}
@@ -801,12 +837,17 @@ static int expand_dereference(struct expression *expr)
expr->type = EXPR_VALUE;
expr->value = value->value;
expr->taint = 0;
+ if ((sym->ctype.modifiers & MOD_CONSTEXPR) &&
+ is_int_type(ctype))
+ expr->flags = CEF_SET_ICE;
return 0;
} else if (value->type == EXPR_FVALUE) {
if (!is_float_type(ctype))
return UNSAFE;
expr->type = EXPR_FVALUE;
expr->fvalue = value->fvalue;
+ if (sym->ctype.modifiers & MOD_CONSTEXPR)
+ expr->flags = CEF_SET_FLOAT;
return 0;
}
}
@@ -1086,6 +1127,11 @@ static unsigned long bit_offset(const struct expression *expr)
offset += bytes_to_bits(expr->init_offset);
expr = expr->init_expr;
}
+ if (expr && expr->type == EXPR_INITIALIZER) {
+ struct expression *first = first_ptr_list((struct ptr_list *)expr->expr_list);
+ if (first)
+ return offset + bit_offset(first);
+ }
if (expr && expr->ctype)
offset += expr->ctype->bit_offset;
return offset;
@@ -1537,3 +1583,11 @@ int is_zero_constant(struct expression *expr)
conservative = saved;
return expr->type == EXPR_VALUE && !expr->value;
}
+
+void expand_constexpr_initializer(struct expression *expr)
+{
+ const int saved = conservative;
+ conservative = 1;
+ expand_expression(expr);
+ conservative = saved;
+}
diff --git a/expand.h b/expand.h
index 27e10c0a87e3..44b300b2a36e 100644
--- a/expand.h
+++ b/expand.h
@@ -31,4 +31,7 @@
#define SELECT_COST 20 /* Cut-off for turning a conditional into a select */
#define BRANCH_COST 10 /* Cost of a conditional branch */
+struct expression;
+void expand_constexpr_initializer(struct expression *expr);
+
#endif
diff --git a/ident-list.h b/ident-list.h
index 18dabf5193b2..46e6cd9e4f61 100644
--- a/ident-list.h
+++ b/ident-list.h
@@ -47,6 +47,7 @@ IDENT(_Thread_local);
IDENT(alignas);
IDENT(alignof);
IDENT(bool);
+IDENT(constexpr);
IDENT(false);
IDENT(nullptr);
IDENT(static_assert);
diff --git a/parse.c b/parse.c
index 324da8e40423..a7d31fad1a10 100644
--- a/parse.c
+++ b/parse.c
@@ -57,7 +57,7 @@ static declarator_t
attribute_specifier, typeof_specifier, typeof_unqual_specifier,
storage_specifier, thread_specifier;
static declarator_t generic_qualifier;
-static declarator_t autotype_specifier, auto_specifier;
+static declarator_t autotype_specifier, auto_specifier, constexpr_specifier;
static struct token *parse_if_statement(struct token *token, struct statement *stmt);
static struct token *parse_return_statement(struct token *token, struct statement *stmt);
@@ -146,6 +146,11 @@ static struct symbol_op auto_op = {
.declarator = auto_specifier,
};
+static struct symbol_op constexpr_op = {
+ .type = KW_MODIFIER,
+ .declarator = constexpr_specifier,
+};
+
static struct symbol_op register_op = {
.type = KW_MODIFIER,
.declarator = storage_specifier,
@@ -479,6 +484,7 @@ static struct init_keyword {
N("__thread", &thread_op),
N("_Thread_local", &thread_op),
N("thread_local", &thread_op, .std = STANDARD_C23),
+ N("constexpr", &constexpr_op, .std = STANDARD_C23),
A("inline", &inline_op, .mods = MOD_INLINE),
@@ -1211,6 +1217,37 @@ static struct token *auto_specifier(struct token *token, struct symbol *sym,
return token;
}
+static struct token *constexpr_specifier(struct token *token,
+ struct symbol *sym,
+ struct decl_state *ctx)
+{
+ if (ctx->is_constexpr)
+ sparse_error(token->pos, "duplicate constexpr");
+ ctx->is_constexpr = 1;
+ ctx->ctype.modifiers |= MOD_CONSTEXPR;
+ return token;
+}
+
+static void apply_constexpr_const(struct symbol *sym)
+{
+ struct ctype *ctype;
+
+ if (!sym)
+ return;
+ sym->ctype.modifiers |= MOD_CONST;
+ ctype = &sym->ctype;
+ while (ctype->base_type) {
+ struct symbol *base = ctype->base_type;
+ if (base->type != SYM_NODE && base->type != SYM_ARRAY)
+ break;
+ struct symbol *copy = alloc_symbol(base->pos, base->type);
+ *copy = *base;
+ copy->ctype.modifiers |= MOD_CONST;
+ ctype->base_type = copy;
+ ctype = ©->ctype;
+ }
+}
+
static struct token *ignore_attribute(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
struct expression *expr = NULL;
@@ -2119,6 +2156,11 @@ static struct token *declaration_list(struct token *token, struct symbol_list **
if (ctx.autotype)
decl->ctype.base_type = &bad_ctype;
}
+ if (ctx.is_constexpr)
+ sparse_error(
+ decl->pos,
+ "struct or union member '%s' cannot be declared constexpr",
+ show_ident(decl->ident));
if (decl->ctype.modifiers & MOD_TLS)
sparse_error(
decl->pos,
@@ -2166,10 +2208,6 @@ static struct token *parameter_declaration(struct token *token, struct symbol *s
sym->ctype.modifiers |= decl_modifiers(&ctx);
sym->endpos = token->pos;
sym->forced_arg = ctx.forced;
- if (ctx.has_alignas)
- sparse_error(sym->pos,
- "alignment specified for parameter '%s'",
- show_ident(sym->ident));
if (ctx.has_auto || ctx.autotype) {
sparse_error(sym->pos,
"parameter '%s' cannot be declared %s",
@@ -2178,6 +2216,14 @@ static struct token *parameter_declaration(struct token *token, struct symbol *s
if (ctx.autotype)
sym->ctype.base_type = &bad_ctype;
}
+ if (ctx.is_constexpr)
+ sparse_error(sym->pos,
+ "parameter '%s' cannot be declared constexpr",
+ show_ident(sym->ident));
+ if (ctx.has_alignas)
+ sparse_error(sym->pos,
+ "alignment specified for parameter '%s'",
+ show_ident(sym->ident));
if (sym->ctype.modifiers & MOD_TLS)
sparse_error(sym->pos,
"parameter '%s' cannot be thread-local",
@@ -2203,6 +2249,8 @@ struct token *typename(struct token *token, struct symbol **p, int *forced)
apply_modifiers(token->pos, &ctx);
sym->ctype = ctx.ctype;
sym->endpos = token->pos;
+ if (ctx.is_constexpr)
+ sparse_error(sym->pos, "constexpr in typename");
class = ctx.storage_class;
if (forced)
*forced = ctx.forced;
@@ -3264,6 +3312,9 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
if (ctx.autotype)
sparse_error(token->pos, "%s in empty declaration",
ctx.has_auto ? "auto" : "__auto_type");
+ if (ctx.is_constexpr)
+ sparse_error(token->pos,
+ "constexpr in empty declaration");
apply_modifiers(token->pos, &ctx);
return token->next;
}
@@ -3294,6 +3345,10 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
decl->ctype.modifiers |= MOD_USERTYPE;
base_type = decl->ctype.base_type;
+ if (ctx.is_constexpr && !is_typedef &&
+ (!base_type || base_type->type != SYM_FN))
+ apply_constexpr_const(decl);
+
if (!is_typedef && ctx.autotype && !ctx.has_auto &&
base_type == &autotype_ctype) {
struct symbol *prev = lookup_symbol(ident, NS_SYMBOL | NS_TYPEDEF);
@@ -3317,6 +3372,12 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
}
}
} else if (base_type && base_type->type == SYM_FN) {
+ if (ctx.is_constexpr) {
+ sparse_error(
+ decl->pos,
+ "function '%s' cannot be declared constexpr",
+ show_ident(decl->ident));
+ }
if (base_type->ctype.base_type == &autotype_ctype) {
sparse_error(decl->pos, "'%s()' has %s return type",
show_ident(decl->ident),
@@ -3360,7 +3421,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
for (;;) {
if (!is_typedef && match_op(token, '=')) {
struct token *next = token->next;
- bool mark_underspec = decl->c23_auto;
+ bool mark_underspec = decl->c23_auto || ctx.is_constexpr;
if (mark_underspec)
decl->underspecified = 1;
token = initializer(&decl->initializer, next);
@@ -3403,7 +3464,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
}
}
- if (!is_typedef && decl->c23_auto &&
+ if (!is_typedef && (decl->c23_auto || ctx.is_constexpr) &&
(!decl->ctype.base_type ||
decl->ctype.base_type->type != SYM_FN)) {
struct symbol *prev;
@@ -3452,6 +3513,34 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
}
}
+ if (ctx.is_constexpr) {
+ if (is_typedef)
+ sparse_error(
+ decl->pos,
+ "constexpr cannot be used with typedef");
+ else if (ctx.storage_class & MOD_EXTERN)
+ sparse_error(
+ decl->pos,
+ "constexpr cannot be used with extern");
+ else if ((ctx.storage_class & MOD_REGISTER) &&
+ toplevel(block_scope))
+ sparse_error(
+ decl->pos,
+ "constexpr register variable '%s' at file scope",
+ show_ident(decl->ident));
+ else if (decl->ctype.modifiers & MOD_TLS)
+ sparse_error(
+ decl->pos,
+ "constexpr cannot be used with thread_local");
+ else if (!decl->initializer &&
+ (!decl->ctype.base_type ||
+ decl->ctype.base_type->type != SYM_FN))
+ sparse_error(
+ decl->pos,
+ "constexpr variable '%s' without initializer",
+ show_ident(decl->ident));
+ }
+
if (ctx.has_alignas) {
if (is_typedef)
sparse_error(decl->pos,
@@ -3507,6 +3596,11 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
if (is_typedef)
decl->ctype.modifiers |= MOD_USERTYPE;
+ base_type = decl->ctype.base_type;
+ if (ctx.is_constexpr && !is_typedef &&
+ (!base_type || base_type->type != SYM_FN))
+ apply_constexpr_const(decl);
+
bind_symbol(decl, ident, is_typedef ? NS_TYPEDEF: NS_SYMBOL);
/* Function declarations are automatically extern unless specifically static */
diff --git a/show-parse.c b/show-parse.c
index 78d86e20e93a..0a277ef67aac 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -119,31 +119,32 @@ static const char *show_modifiers(unsigned long mod, int term)
} *m;
static struct mod_name mod_names[] = {
- {MOD_AUTO, "auto"},
- {MOD_EXTERN, "extern"},
- {MOD_REGISTER, "register"},
- {MOD_STATIC, "static"},
- {MOD_INLINE, "inline"},
- {MOD_CONST, "const"},
- {MOD_RESTRICT, "restrict"},
- {MOD_VOLATILE, "volatile"},
- {MOD_ADDRESSABLE, "[addressable]"},
- {MOD_ASSIGNED, "[assigned]"},
- {MOD_ATOMIC, "[atomic]"},
- {MOD_BITWISE, "[bitwise]"},
- {MOD_EXPLICITLY_SIGNED, "[explicitly-signed]"},
- {MOD_GNU_INLINE, "[gnu_inline]"},
- {MOD_NOCAST, "[nocast]"},
- {MOD_NODEREF, "[noderef]"},
- {MOD_NORETURN, "[noreturn]"},
- {MOD_PURE, "[pure]"},
- {MOD_SAFE, "[safe]"},
- {MOD_SIGNED, "[signed]"},
- {MOD_TLS, "[tls]"},
- {MOD_TOPLEVEL, "[toplevel]"},
- {MOD_UNSIGNED, "[unsigned]"},
- {MOD_UNUSED, "[unused]"},
- {MOD_USERTYPE, "[usertype]"},
+ { MOD_AUTO, "auto" },
+ { MOD_EXTERN, "extern" },
+ { MOD_REGISTER, "register" },
+ { MOD_STATIC, "static" },
+ { MOD_CONSTEXPR, "constexpr" },
+ { MOD_INLINE, "inline" },
+ { MOD_CONST, "const" },
+ { MOD_RESTRICT, "restrict" },
+ { MOD_VOLATILE, "volatile" },
+ { MOD_ADDRESSABLE, "[addressable]" },
+ { MOD_ASSIGNED, "[assigned]" },
+ { MOD_ATOMIC, "[atomic]" },
+ { MOD_BITWISE, "[bitwise]" },
+ { MOD_EXPLICITLY_SIGNED, "[explicitly-signed]" },
+ { MOD_GNU_INLINE, "[gnu_inline]" },
+ { MOD_NOCAST, "[nocast]" },
+ { MOD_NODEREF, "[noderef]" },
+ { MOD_NORETURN, "[noreturn]" },
+ { MOD_PURE, "[pure]" },
+ { MOD_SAFE, "[safe]" },
+ { MOD_SIGNED, "[signed]" },
+ { MOD_TLS, "[tls]" },
+ { MOD_TOPLEVEL, "[toplevel]" },
+ { MOD_UNSIGNED, "[unsigned]" },
+ { MOD_UNUSED, "[unused]" },
+ { MOD_USERTYPE, "[usertype]" },
};
for (i = 0; i < ARRAY_SIZE(mod_names); i++) {
diff --git a/symbol.c b/symbol.c
index b0bdf0b1122f..fc11e1bbcec3 100644
--- a/symbol.c
+++ b/symbol.c
@@ -563,7 +563,7 @@ static struct symbol * examine_node_type(struct symbol *sym)
sym->ctype.alignment = alignment;
/* Unsized array? The size might come from the initializer.. */
- if (bit_size < 0 && base_type->type == SYM_ARRAY) {
+ if (bit_size < 0 && base_type->type == SYM_ARRAY && !base_type->array_size) {
struct expression *initializer = get_symbol_initializer(sym);
if (initializer) {
struct symbol *node_type = base_type->ctype.base_type;
@@ -872,6 +872,10 @@ void bind_symbol_with_scope(struct symbol *sym, struct ident *ident, enum namesp
if (ns == NS_SYMBOL && toplevel(scope)) {
unsigned mod = MOD_ADDRESSABLE | MOD_TOPLEVEL;
+ if ((sym->ctype.modifiers & MOD_CONSTEXPR) &&
+ !(sym->ctype.modifiers & MOD_EXTERN))
+ sym->ctype.modifiers |= MOD_STATIC;
+
inherit_static(sym);
scope = global_scope;
diff --git a/symbol.h b/symbol.h
index 61ca70afe83b..f8556fdf3b86 100644
--- a/symbol.h
+++ b/symbol.h
@@ -117,6 +117,7 @@ struct decl_state {
unsigned char packed;
unsigned char has_auto;
unsigned char has_alignas;
+ unsigned char is_constexpr;
unsigned char designated_init;
unsigned char transparent_union;
};
@@ -254,10 +255,10 @@ struct symbol {
#define MOD_GNU_INLINE 0x00010000
#define MOD_USERTYPE 0x00020000
- // MOD UNUSED 0x00040000
- // MOD UNUSED 0x00080000
- // MOD UNUSED 0x00100000
- // MOD UNUSED 0x00200000
+#define MOD_CONSTEXPR 0x00040000
+// MOD UNUSED 0x00080000
+// MOD UNUSED 0x00100000
+// MOD UNUSED 0x00200000
#define MOD_UNUSED 0x00400000
#define MOD_SAFE 0x00800000 // non-null/non-trapping pointer
@@ -275,7 +276,7 @@ struct symbol {
#define MOD_ESIGNED (MOD_SIGNED | MOD_EXPLICITLY_SIGNED)
#define MOD_SIGNEDNESS (MOD_SIGNED | MOD_UNSIGNED | MOD_EXPLICITLY_SIGNED)
#define MOD_SPECIFIER MOD_SIGNEDNESS
-#define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE | MOD_UNUSED | MOD_GNU_INLINE)
+#define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE | MOD_UNUSED | MOD_GNU_INLINE | MOD_CONSTEXPR)
#define MOD_QUALIFIER (MOD_CONST | MOD_VOLATILE | MOD_RESTRICT)
#define MOD_PTRINHERIT (MOD_QUALIFIER | MOD_ATOMIC | MOD_NODEREF | MOD_NORETURN | MOD_NOCAST)
/* modifiers preserved by typeof() operator */
@@ -287,9 +288,9 @@ struct symbol {
/* do not warn when these are duplicated */
#define MOD_DUP_OK (MOD_UNUSED|MOD_GNU_INLINE)
/* must be part of the declared symbol, not its type */
-#define MOD_DECLARE (MOD_STORAGE|MOD_INLINE|MOD_TLS|MOD_GNU_INLINE|MOD_UNUSED|MOD_PURE|MOD_NORETURN|MOD_EXT_VISIBLE)
-
-
+#define MOD_DECLARE \
+ (MOD_STORAGE | MOD_INLINE | MOD_TLS | MOD_GNU_INLINE | MOD_UNUSED | \
+ MOD_PURE | MOD_NORETURN | MOD_EXT_VISIBLE | MOD_CONSTEXPR)
/* Current parsing/evaluation function */
extern struct symbol *current_fn;
diff --git a/validation/c23-constexpr-error.c b/validation/c23-constexpr-error.c
new file mode 100644
index 000000000000..41ee86d7ae63
--- /dev/null
+++ b/validation/c23-constexpr-error.c
@@ -0,0 +1,92 @@
+constexpr int no_init;
+constexpr int foo(void)
+{
+ return 0;
+}
+typedef constexpr int my_int;
+extern constexpr int ext = 1;
+register constexpr int reg = 1;
+void bar(constexpr int p)
+{
+}
+struct s {
+ constexpr int m;
+};
+constexpr constexpr int double_ce = 1;
+int non_const = 5;
+constexpr int bad_init = non_const;
+thread_local constexpr int tls = 1;
+constexpr struct empty_s {
+ int a;
+};
+constexpr volatile int cv = 1;
+constexpr _Atomic int ca = 1;
+constexpr int *restrict cr = nullptr;
+constexpr int *bad_ptr = &non_const;
+constexpr const char *bad_str = "hi";
+constexpr int bad_flt_ice = 1.0;
+constexpr unsigned int neg_to_u = -1;
+constexpr signed char over_sc = 200;
+constexpr bool bad_bool = 2;
+constexpr float inexact_int = 16777217;
+constexpr float inexact_dbl = 0.1;
+extern int already_in_scope;
+constexpr int already_in_scope = 1;
+
+struct pwrap {
+ int *p;
+ unsigned char u;
+};
+constexpr struct pwrap bad_nested = { &non_const, 300 };
+
+void assign_and_vla_test(int n)
+{
+ constexpr int c = 10;
+ c = 20;
+
+ constexpr int *cp = nullptr;
+ cp = nullptr;
+
+ constexpr int arr[2] = { 1, 2 };
+ arr[0] = 3;
+
+ constexpr int (*vla)[n] = nullptr;
+ (void)vla;
+}
+
+/*
+ * check-name: C23 constexpr error cases
+ * check-command: sparse -std=c23 $file
+ *
+ * check-error-start
+c23-constexpr-error.c:1:15: error: constexpr variable 'no_init' without initializer
+c23-constexpr-error.c:2:15: error: function 'foo' cannot be declared constexpr
+c23-constexpr-error.c:6:23: error: constexpr cannot be used with typedef
+c23-constexpr-error.c:7:22: error: constexpr cannot be used with extern
+c23-constexpr-error.c:8:24: error: constexpr register variable 'reg' at file scope
+c23-constexpr-error.c:9:10: error: parameter 'p' cannot be declared constexpr
+c23-constexpr-error.c:13:23: error: struct or union member 'm' cannot be declared constexpr
+c23-constexpr-error.c:15:21: error: duplicate constexpr
+c23-constexpr-error.c:18:28: error: constexpr cannot be used with thread_local
+c23-constexpr-error.c:21:2: error: constexpr in empty declaration
+c23-constexpr-error.c:34:15: error: underspecified declaration of 'already_in_scope', which is already declared in this scope
+c23-constexpr-error.c:17:26: error: non-constant initializer for constexpr object
+c23-constexpr-error.c:22:24: error: constexpr variable 'cv' cannot have volatile, atomic, or restrict type
+c23-constexpr-error.c:23:23: error: constexpr variable 'ca' cannot have volatile, atomic, or restrict type
+c23-constexpr-error.c:24:15: error: constexpr variable 'cr' cannot have volatile, atomic, or restrict type
+c23-constexpr-error.c:25:27: error: constexpr pointer initializer is not null
+c23-constexpr-error.c:26:33: error: constexpr pointer initializer is not null
+c23-constexpr-error.c:27:29: error: constexpr integer initializer is not an integer constant expression
+c23-constexpr-error.c:28:35: error: constexpr initializer not representable in target type
+c23-constexpr-error.c:29:33: error: constexpr initializer not representable in target type
+c23-constexpr-error.c:30:27: error: constexpr initializer not representable in target type
+c23-constexpr-error.c:31:31: error: constexpr initializer not representable in target type
+c23-constexpr-error.c:32:31: error: constexpr initializer not representable in target type
+c23-constexpr-error.c:40:40: error: constexpr pointer initializer is not null
+c23-constexpr-error.c:40:51: error: constexpr initializer not representable in target type
+c23-constexpr-error.c:45:9: error: assignment to const expression
+c23-constexpr-error.c:48:9: error: assignment to const expression
+c23-constexpr-error.c:51:12: error: assignment to const expression
+c23-constexpr-error.c:53:23: error: constexpr variable 'vla' cannot have variably modified type
+ * check-error-end
+ */
diff --git a/validation/c23-constexpr-self-init.c b/validation/c23-constexpr-self-init.c
new file mode 100644
index 000000000000..2af16572a778
--- /dev/null
+++ b/validation/c23-constexpr-self-init.c
@@ -0,0 +1,41 @@
+/*
+ * Verify that self-referencing constexpr initializers do not cause
+ * infinite recursion or stack overflow in constant_symbol_value().
+ */
+
+static constexpr int a = a;
+static constexpr int b = b + 1;
+
+static void test_local(void)
+{
+ constexpr int x = x;
+ constexpr int y = y + 1;
+ int c = 5;
+ {
+ constexpr int c = c;
+ (void)c;
+ }
+ constexpr int sz = sizeof(sz);
+ (void)sz;
+ (void)c;
+}
+
+static void test_eval(void)
+{
+ _Static_assert(b == 0, "");
+}
+
+/*
+ * check-name: C23 constexpr self-initialization recursion
+ * check-command: sparse -std=c23 $file
+ *
+ * check-error-start
+c23-constexpr-self-init.c:7:26: error: underspecified symbol 'b' used in its own initializer
+c23-constexpr-self-init.c:25:26: error: bad constant expression
+c23-constexpr-self-init.c:6:26: error: underspecified symbol 'a' used in its own initializer
+c23-constexpr-self-init.c:11:27: error: underspecified symbol 'x' used in its own initializer
+c23-constexpr-self-init.c:12:27: error: underspecified symbol 'y' used in its own initializer
+c23-constexpr-self-init.c:15:35: error: underspecified symbol 'c' used in its own initializer
+c23-constexpr-self-init.c:18:35: error: underspecified symbol 'sz' used in its own initializer
+ * check-error-end
+ */
diff --git a/validation/c23-constexpr.c b/validation/c23-constexpr.c
new file mode 100644
index 000000000000..ec0cf227154f
--- /dev/null
+++ b/validation/c23-constexpr.c
@@ -0,0 +1,100 @@
+#ifdef __CHECKER__
+#define ASSERT_TYPE(expr, T) _Static_assert([typeof(expr)] == [T], "")
+#else
+#define ASSERT_TYPE(expr, T) \
+ _Static_assert(_Generic((typeof(expr) *)0, typeof(T) *: 1, default: 0), "")
+#endif
+
+static constexpr int N = 5;
+static constexpr auto A = 10;
+static constexpr double D = 2.5;
+static constexpr int TRUNC = (int)D;
+constexpr int FILE_CE = 7;
+
+struct inner_c {
+ int a;
+ int b;
+};
+
+struct outer_c {
+ int x;
+ struct inner_c in;
+};
+
+static constexpr struct outer_c OUTER = { 1, { 2, 42 } };
+static constexpr struct outer_c OUTER_DESIG = { .in.b = 99 };
+static constexpr struct outer_c OUTER_COPY = OUTER;
+
+typedef int pair_t[2];
+static constexpr pair_t CE_PAIR = { 10, 20 };
+
+static constexpr int *NP = nullptr;
+static constexpr const int *CNP = (void *)0;
+static constexpr typeof(nullptr) NULLP = nullptr;
+static constexpr int *NP2 = NP;
+
+static constexpr char STR[] = "ok";
+static constexpr bool B0 = 0;
+static constexpr bool B1 = true;
+static constexpr float EXACT_F = 16777216;
+
+struct large_c {
+ int pad[1024];
+ int tail;
+};
+
+static constexpr struct large_c LARGE = { .tail = 77 };
+
+enum my_enum { ENUM_VAL = N * 2 };
+
+struct bitfield_test {
+ int b : N;
+};
+
+static int array[N];
+
+static void test(int val)
+{
+ static_assert(N == 5);
+ static_assert(A == 10);
+ static_assert(TRUNC == 2);
+ static_assert(FILE_CE == 7);
+ static_assert(OUTER.in.b == 42);
+ static_assert(OUTER_DESIG.in.b == 99);
+ static_assert(OUTER_COPY.in.b == 42);
+ static_assert(LARGE.tail == 77);
+ static_assert(sizeof(array) == 5 * sizeof(int));
+ static_assert(ENUM_VAL == 10);
+ static_assert(sizeof(STR) == 3);
+ static_assert(!B0 && B1);
+ static_assert((int)EXACT_F == 16777216);
+
+ ASSERT_TYPE(A, const int);
+ ASSERT_TYPE(NP, int *const);
+ ASSERT_TYPE(CNP, const int *const);
+ ASSERT_TYPE(NULLP, const typeof(nullptr));
+ ASSERT_TYPE(NP2, int *const);
+ ASSERT_TYPE(CE_PAIR[0], const int);
+
+ /* Ensure typedef pair_t itself was not mutated to const */
+ pair_t mut_pair = { 1, 2 };
+ mut_pair[0] = 3;
+
+ constexpr int LOCAL = 3;
+ constexpr register int REG_LOCAL = 4;
+ static_assert(LOCAL == 3);
+ static_assert(REG_LOCAL == 4);
+ switch (val) {
+ case LOCAL:
+ case REG_LOCAL:
+ break;
+ }
+
+ const int *p = &N;
+ (void)p;
+}
+
+/*
+ * check-name: C23 constexpr object declarations
+ * check-command: sparse -std=c23 $file
+ */
next prev parent reply other threads:[~2026-11-09 20:34 UTC|newest]
Thread overview: 74+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 20:31 Bart Van Assche
2026-11-09 20:31 ` [PATCH 01/73] Makefile: Pass '--' to rm in clean target to handle dash-prefixed files Bart Van Assche
2026-11-09 20:31 ` [PATCH 02/73] sparse-llvm: Use LLVMCreateBuilderForModule() with LLVM 24 and later Bart Van Assche
2026-11-09 20:31 ` [PATCH 03/73] typeof_unqual: Strip qualifiers and address space from types and arrays Bart Van Assche
2026-11-09 20:31 ` [PATCH 04/73] bitfield: Pack consecutive _Bool bitfields into byte containers Bart Van Assche
2026-11-09 20:31 ` [PATCH 05/73] allocate: Harden slab allocation bounds, zeroing, and mmap checks Bart Van Assche
2026-11-09 20:31 ` [PATCH 06/73] tokenize: Support 4096-byte identifiers and fix escape bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 07/73] pre-process: Fix macro expansion and include directive handling Bart Van Assche
2026-11-09 20:31 ` [PATCH 08/73] options, lib: Harden warning buffers and command-line option parsing Bart Van Assche
2026-11-09 20:31 ` [PATCH 09/73] builtin: Fix atomic builtin type checking and object_size bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 10/73] parse, show-parse: Fix attribute propagation, enum overflow, and bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 11/73] target-openrisc: Set target_openrisc.mach to MACH_OPENRISC Bart Van Assche
2026-11-09 20:31 ` [PATCH 12/73] evaluate: Fix pointer arithmetic, incomplete types, and qualifiers Bart Van Assche
2026-11-09 20:31 ` [PATCH 13/73] expand: Fix static initializer folding and floating-point casts Bart Van Assche
2026-11-09 20:32 ` [PATCH 14/73] inline: Copy union cast types and VLA bounds when inlining Bart Van Assche
2026-11-09 20:32 ` [PATCH 15/73] linearize, flow: Fix asm operands, switch case bounds, and DSE Bart Van Assche
2026-11-09 20:32 ` [PATCH 16/73] memops, ssa: Walk parent insns in reverse and skip mixed-size SSA Bart Van Assche
2026-11-09 20:32 ` [PATCH 17/73] simplify: Prevent overflow on LLONG_MIN negation and fix simplifications Bart Van Assche
2026-11-09 20:32 ` [PATCH 18/73] sparse: Gate context failure warning on Wcontext and catch underflow Bart Van Assche
2026-11-09 20:32 ` [PATCH 19/73] ast-model: Return AST_N_COLUMNS and inspect child nodes lazily Bart Van Assche
2026-11-09 20:32 ` [PATCH 20/73] c2xml: Guard against recursive symbols and free xmlGetProp() string Bart Van Assche
2026-11-09 20:32 ` [PATCH 21/73] compile-i386: Fix buffer bounds, unsigned '<=' codegen, and null checks Bart Van Assche
2026-11-09 20:32 ` [PATCH 22/73] ctags: Fix line comparison order and handle SYM_RESTRICT and SYM_LABEL Bart Van Assche
2026-11-09 20:32 ` [PATCH 23/73] dissect: Unwrap array element type via base_type() Bart Van Assche
2026-11-09 20:32 ` [PATCH 24/73] example: Fix show_op() rotating buffer dimensions Bart Van Assche
2026-11-09 20:32 ` [PATCH 25/73] graph: Check PSEUDO_SYM before dereferencing insn->func->sym Bart Van Assche
2026-11-09 20:32 ` [PATCH 26/73] scheck: Fix slice() upper bit index calculation Bart Van Assche
2026-11-09 20:32 ` [PATCH 27/73] sparse-llvm: Support arbitrary struct member counts and fix operand types Bart Van Assche
2026-11-09 20:32 ` [PATCH 28/73] inline: Duplicate generic expressions instead of evaluating them Bart Van Assche
2026-11-09 20:32 ` [PATCH 29/73] lib: Avoid mutating fmt before va_start() in warning() Bart Van Assche
2026-11-09 20:32 ` [PATCH 30/73] cse, canon: Cast pointers to uintptr_t before relational comparison Bart Van Assche
2026-11-09 20:32 ` [PATCH 31/73] context: Add "conditional context increment" model Bart Van Assche
2026-11-09 20:32 ` [PATCH 32/73] expand: Simplify signed constants early Bart Van Assche
2026-11-09 20:32 ` [PATCH 33/73] bitwise: Allow all-bits-set as a restricted value Bart Van Assche
2026-11-09 20:32 ` [PATCH 34/73] bitwise: Allow restricted ordered compares with unrestricted values Bart Van Assche
2026-11-09 20:32 ` [PATCH 35/73] bitwise: Keep unrestricted values unrestricted after a cast Bart Van Assche
2026-11-09 20:32 ` [PATCH 36/73] bitwise: Don't remove MOD_SPECIFIER from 'bitwise' types Bart Van Assche
2026-11-09 20:32 ` [PATCH 37/73] bitwise: Allow both regular casts and forced casts for restricted values Bart Van Assche
2026-11-09 20:32 ` [PATCH 38/73] clang-format: Add .clang-format configuration file Bart Van Assche
2026-11-09 20:32 ` [PATCH 39/73] options: Add support for -std=c23, -std=c2x and -std=gnu23 Bart Van Assche
2026-11-09 20:32 ` [PATCH 40/73] preprocessor: Add support for #elifdef and #elifndef Bart Van Assche
2026-11-09 20:32 ` [PATCH 41/73] preprocessor: Add support for __has_include and __has_include_next Bart Van Assche
2026-11-09 20:32 ` [PATCH 42/73] c23: Add support for static_assert, alignas, alignof, thread_local Bart Van Assche
2026-11-09 20:32 ` [PATCH 43/73] c23: Add support for bool, true, and false keywords Bart Van Assche
2026-11-09 20:32 ` [PATCH 44/73] c23: Add support for nullptr and nullptr_t Bart Van Assche
2026-11-09 20:32 ` [PATCH 45/73] c23: Add support for digit separators in numeric literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 46/73] c23: Support unnamed parameters in function definitions Bart Van Assche
2026-11-09 20:32 ` [PATCH 47/73] c23: Support variadic functions without named parameters Bart Van Assche
2026-11-09 20:32 ` [PATCH 48/73] c23: Support labels before declarations and at block ends Bart Van Assche
2026-11-09 20:32 ` [PATCH 49/73] c23: Support auto type inference Bart Van Assche
2026-11-09 20:32 ` Bart Van Assche [this message]
2026-11-09 20:32 ` [PATCH 51/73] c23: Support enumerations with fixed underlying types Bart Van Assche
2026-11-09 20:32 ` [PATCH 52/73] c23: Support standard [[]] attribute syntax Bart Van Assche
2026-11-09 20:32 ` [PATCH 53/73] preprocessor: Add support for __has_c_attribute Bart Van Assche
2026-11-09 20:32 ` [PATCH 54/73] c23: Support empty initializer braces {} Bart Van Assche
2026-11-09 20:32 ` [PATCH 55/73] preprocessor: Add support for __has_embed Bart Van Assche
2026-11-09 20:32 ` [PATCH 56/73] preprocessor: Add support for #embed directive Bart Van Assche
2026-11-09 20:32 ` [PATCH 57/73] c23: Add support for u8 character constants and string literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 58/73] preprocessor: Add support for __VA_OPT__ Bart Van Assche
2026-11-09 20:32 ` [PATCH 59/73] c23: Treat empty parameter list () as (void) and reject K&R functions Bart Van Assche
2026-11-09 20:32 ` [PATCH 60/73] c23: Support storage-class specifiers in compound literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 61/73] c23: Add support for _BitInt(N) Bart Van Assche
2026-11-09 20:32 ` [PATCH 62/73] enum: Support machine mode attributes on enumeration types Bart Van Assche
2026-11-09 20:32 ` [PATCH 63/73] enum: Support __attribute__((packed)) " Bart Van Assche
2026-11-09 20:32 ` [PATCH 64/73] expand: Ignore top-level non-array address space in compare_types() Bart Van Assche
2026-11-09 20:32 ` [PATCH 65/73] builtin: Support __builtin_infer_alloc_token() as a size_t ICE Bart Van Assche
2026-11-09 20:32 ` [PATCH 66/73] builtin: Add support for Linux kernel compiler builtins Bart Van Assche
2026-11-09 20:32 ` [PATCH 67/73] builtin: Add support for __builtin_has_attribute() Bart Van Assche
2026-11-09 20:32 ` [PATCH 68/73] cleanup: Support __attribute__((cleanup(...))) in linearize and -Wcontext Bart Van Assche
2026-11-09 20:32 ` [PATCH 69/73] builtin: Do not evaluate __builtin_clz(0) and __builtin_ctz(0) Bart Van Assche
2026-11-09 20:32 ` [PATCH 70/73] pre-process: Support long token and string expansions dynamically Bart Van Assche
2026-11-09 20:32 ` [PATCH 71/73] x86: Undefine conflicting bitness macros when parsing -m flags Bart Van Assche
2026-11-09 20:32 ` [PATCH 72/73] evaluate: Accept conditional expressions with only one void side Bart Van Assche
2026-11-09 20:32 ` [PATCH 73/73] ci: Add Linux kernel GitHub Actions workflow and build script Bart Van Assche
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109203259.362845-51-bvanassche@acm.org \
--to=bvanassche@acm.org \
--cc=linux-sparse@vger.kernel.org \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox