Linux SPARSE checker discussions
 help / color / mirror / Atom feed
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 = &copy->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
+ */

  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