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 55/73] preprocessor: Add support for __has_embed
Date: Mon,  9 Nov 2026 12:32:41 -0800	[thread overview]
Message-ID: <20261109203259.362845-56-bvanassche@acm.org> (raw)
In-Reply-To: <20261109203259.362845-1-bvanassche@acm.org>

Add support for the C23 __has_embed preprocessor operator to check resource
availability and parameters prior to inclusion via #embed. Predefine the
__STDC_EMBED_NOT_FOUND__, __STDC_EMBED_FOUND__, and __STDC_EMBED_EMPTY__
constants to allow portable feature detection in preprocessor expressions.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 ident-list.h                            |   9 +
 pre-process.c                           | 445 +++++++++++++++++++++++-
 predefine.c                             |   3 +
 validation/preprocessor/c23-has-embed.c |  69 ++++
 4 files changed, 522 insertions(+), 4 deletions(-)
 create mode 100644 validation/preprocessor/c23-has-embed.c

diff --git a/ident-list.h b/ident-list.h
index bc5df947913a..f51f2048f5ca 100644
--- a/ident-list.h
+++ b/ident-list.h
@@ -84,6 +84,15 @@ __IDENT(__FUNCTION___ident, "__FUNCTION__", 0);
 __IDENT(__PRETTY_FUNCTION___ident, "__PRETTY_FUNCTION__", 0);
 __IDENT(__has_include_ident, "__has_include", 0);
 __IDENT(__has_include_next_ident, "__has_include_next", 0);
+__IDENT(__has_embed_ident, "__has_embed", 0);
+IDENT(limit);
+IDENT(prefix);
+IDENT(suffix);
+IDENT(if_empty);
+__IDENT(__limit___ident, "__limit__", 0);
+__IDENT(__prefix___ident, "__prefix__", 0);
+__IDENT(__suffix___ident, "__suffix__", 0);
+__IDENT(__if_empty___ident, "__if_empty__", 0);
 
 /* Sparse commands */
 IDENT_RESERVED(__context__);
diff --git a/pre-process.c b/pre-process.c
index aac194cf4c01..e8b3511c225e 100644
--- a/pre-process.c
+++ b/pre-process.c
@@ -764,6 +764,7 @@ static struct token **substitute(struct token **list, const struct token *body,
 }
 
 static bool parse_has_include(struct token **list, int how);
+static bool parse_has_embed(struct token **list);
 
 static void reject_outside_directive(struct token *token)
 {
@@ -805,7 +806,8 @@ static int expand(struct token **list, struct symbol *sym)
 	}
 
 	if (expanding == &__has_include_ident ||
-	    expanding == &__has_include_next_ident) {
+	    expanding == &__has_include_next_ident ||
+	    expanding == &__has_embed_ident) {
 		bool ok;
 
 		if (!in_cond_expr) {
@@ -814,8 +816,10 @@ static int expand(struct token **list, struct symbol *sym)
 		}
 		if (expanding == &__has_include_ident)
 			ok = parse_has_include(list, 0);
-		else
+		else if (expanding == &__has_include_next_ident)
 			ok = parse_has_include(list, 1);
+		else
+			ok = parse_has_embed(list);
 		if (!ok)
 			replace_with_bool(token, 0);
 		return 0;
@@ -1099,7 +1103,7 @@ static bool collect_angle_filename(struct token **next_p, char *buf,
 	struct token *t = *next_p;
 	char *ptr = buf;
 
-	while (!eof_token(t) && !match_op(t, '>')) {
+	while (!eof_token(t = scan_next(&t)) && !match_op(t, '>')) {
 		const char *s = (token_type(t) == TOKEN_STRING) ?
 					t->string->data :
 					show_token(t);
@@ -1123,7 +1127,7 @@ static bool collect_angle_filename(struct token **next_p, char *buf,
 		sparse_error(pos, "empty filename");
 		return false;
 	}
-	*next_p = t->next;
+	*next_p = scan_next(&t->next);
 	return true;
 }
 
@@ -1231,6 +1235,432 @@ err:
 	return false;
 }
 
+static void replace_with_int(struct token *token, int val)
+{
+	static const char *const digits[] = { "0", "1", "2" };
+	token_type(token) = TOKEN_NUMBER;
+	token->number = (val >= 0 && val <= 2) ? digits[val] : "0";
+}
+
+#define MAX_EMBED_SIZE (16 * 1024 * 1024)
+
+enum embed_param_kind {
+	EMBED_PARAM_NONE = 0,
+	EMBED_PARAM_LIMIT = 1 << 0,
+	EMBED_PARAM_PREFIX = 1 << 1,
+	EMBED_PARAM_SUFFIX = 1 << 2,
+	EMBED_PARAM_IF_EMPTY = 1 << 3,
+};
+
+static enum embed_param_kind classify_embed_param(struct ident *id)
+{
+	if (id == &limit_ident || id == &__limit___ident)
+		return EMBED_PARAM_LIMIT;
+	if (id == &prefix_ident || id == &__prefix___ident)
+		return EMBED_PARAM_PREFIX;
+	if (id == &suffix_ident || id == &__suffix___ident)
+		return EMBED_PARAM_SUFFIX;
+	if (id == &if_empty_ident || id == &__if_empty___ident)
+		return EMBED_PARAM_IF_EMPTY;
+	return EMBED_PARAM_NONE;
+}
+
+static struct token *dup_token_range(struct token *start, struct token *end)
+{
+	struct token *head = &eof_token_entry;
+	struct token **tail = &head;
+	struct token *p;
+
+	if (!start || !end)
+		return &eof_token_entry;
+	for (p = start;; p = p->next) {
+		struct token *t = __alloc_token(0);
+		*t = *p;
+		*tail = t;
+		tail = &t->next;
+		if (p == end)
+			break;
+	}
+	*tail = &eof_token_entry;
+	return head;
+}
+
+static bool eval_embed_limit_expr(struct position pos, struct token *pstart,
+				  struct token *pend,
+				  unsigned long long *limit_val)
+{
+	struct token *copy = dup_token_range(pstart, pend);
+	struct expression *expr = NULL;
+	long long sval;
+
+	if (eof_token(copy)) {
+		sparse_error(pos, "expected expression in #embed limit");
+		return false;
+	}
+	in_cond_expr++;
+	expand_list(&copy);
+	for (struct token *t = copy; !eof_token(t); t = t->next) {
+		if (token_type(t) != TOKEN_IDENT)
+			continue;
+		if (standard >= STANDARD_C23 && t->ident == &true_ident) {
+			replace_with_bool(t, 1);
+		} else if (standard >= STANDARD_C23 &&
+			   t->ident == &false_ident) {
+			replace_with_bool(t, 0);
+		} else if (t->ident == &defined_ident) {
+			in_cond_expr--;
+			sparse_error(t->pos,
+				     "'defined' cannot appear in #embed limit");
+			return false;
+		} else {
+			token_type(t) = TOKEN_ZERO_IDENT;
+		}
+	}
+	in_cond_expr--;
+	copy = constant_expression(copy, &expr);
+	if (!expr || !eof_token(copy)) {
+		sparse_error(pos, "invalid #embed limit");
+		return false;
+	}
+	sval = get_expression_value(expr);
+	if (!expr->ctype || expr->type != EXPR_VALUE)
+		return false;
+	if (sval < 0 && !(expr->ctype->ctype.modifiers & MOD_UNSIGNED)) {
+		sparse_error(expr->pos, "negative #embed limit");
+		return false;
+	}
+	*limit_val = (unsigned long long)sval;
+	return true;
+}
+
+static int test_embed_file(const char *path, const char *filename,
+			   int has_limit, unsigned long long limit_val,
+			   unsigned long long *size)
+{
+	int fd;
+	struct stat st;
+	size_t plen = strlen(path);
+	size_t flen = strlen(filename);
+	char fullname[PATH_MAX];
+
+	if (plen + flen + 2 > sizeof(fullname))
+		return 0;
+	memcpy(fullname, path, plen);
+	if (plen && path[plen - 1] != '/') {
+		fullname[plen] = '/';
+		plen++;
+	}
+	memcpy(fullname + plen, filename, flen + 1);
+	fd = open(fullname, O_RDONLY | O_NONBLOCK);
+	if (fd < 0)
+		return 0;
+	if (fstat(fd, &st) < 0 || S_ISDIR(st.st_mode)) {
+		close(fd);
+		return 0;
+	}
+	if (S_ISREG(st.st_mode)) {
+		unsigned long long eff = st.st_size;
+
+		close(fd);
+		if (has_limit && limit_val < eff)
+			eff = limit_val;
+		if (eff > MAX_EMBED_SIZE)
+			return 0;
+		*size = eff;
+		return 1;
+	}
+	if (S_ISCHR(st.st_mode)) {
+		unsigned char b;
+		ssize_t r;
+
+		if (has_limit && limit_val == 0) {
+			close(fd);
+			*size = 0;
+			return 1;
+		}
+		if (has_limit && limit_val > MAX_EMBED_SIZE) {
+			close(fd);
+			return 0;
+		}
+		r = read(fd, &b, 1);
+		close(fd);
+		if (r == 0) {
+			*size = 0;
+			return 1;
+		}
+		if (r > 0 && has_limit) {
+			*size = 1;
+			return 1;
+		}
+		return 0;
+	}
+	close(fd);
+	return 0;
+}
+
+static int check_has_embed(struct stream *stream, const char *filename,
+			   int expect, int has_limit,
+			   unsigned long long limit_val,
+			   unsigned long long *size)
+{
+	const char **path;
+
+	/* Absolute path? */
+	if (filename[0] == '/')
+		return test_embed_file("", filename, has_limit, limit_val,
+				       size);
+
+	set_stream_include_path(stream);
+	path = expect ? angle_includepath : quote_includepath;
+	if (!path)
+		return 0;
+	while (*path != NULL) {
+		if (test_embed_file(*path++, filename, has_limit, limit_val,
+				    size))
+			return 1;
+	}
+	return 0;
+}
+
+static bool parse_has_embed(struct token **list)
+{
+	struct token *token = *list;
+	struct token *lparen = scan_next(&token->next);
+	struct token *next;
+	struct token *params, **param_p, *rparen, *after_rparen;
+	struct stream *stream = input_streams + token->pos.stream;
+	char filename[PATH_MAX];
+	int expect = 0;
+	int val = 0;
+	unsigned long long filesize = 0;
+	int has_limit = 0;
+	unsigned long long limit_val = ~0ULL;
+	unsigned int seen_params = 0;
+	int supported = 1;
+
+	if (!match_op(lparen, '(')) {
+		sparse_error(token->pos, "expected '(' after %s",
+			     show_token(token));
+		return false;
+	}
+	next = scan_next(&lparen->next);
+	if (match_op(next, '<')) {
+		expect = '>';
+		next = next->next;
+		if (!collect_angle_filename(&next, filename, sizeof(filename),
+					    token->pos))
+			goto err;
+	} else if (token_type(next) == TOKEN_STRING) {
+		if (!extract_string_filename(&next, filename, sizeof(filename)))
+			goto err;
+	} else {
+		struct token **p = &lparen->next;
+		int depth = 1;
+
+		rparen = scan_next(p);
+		while (!eof_token(rparen)) {
+			if (match_op(rparen, '('))
+				depth++;
+			else if (match_op(rparen, ')') && !--depth)
+				break;
+			p = &rparen->next;
+			rparen = scan_next(p);
+		}
+		if (!match_op(rparen, ')')) {
+			sparse_error(rparen->pos, "expected ')'");
+			token->next = rparen;
+			return false;
+		}
+		*p = &eof_token_entry;
+		expand_list(&lparen->next);
+		p = &lparen->next;
+		while (!eof_token(*p))
+			p = &(*p)->next;
+		*p = rparen;
+		next = lparen->next;
+		if (match_op(next, '<')) {
+			expect = '>';
+			next = next->next;
+			if (!collect_angle_filename(&next, filename,
+						    sizeof(filename),
+						    token->pos))
+				goto err;
+		} else if (token_type(next) == TOKEN_STRING) {
+			if (!extract_string_filename(&next, filename,
+						     sizeof(filename)))
+				goto err;
+		} else {
+			sparse_error(next->pos,
+				     "expected \"FILENAME\" or <FILENAME>");
+			goto err;
+		}
+	}
+
+	/* Isolate optional parameters before matching ')' */
+	params = next;
+	{
+		struct token **p = &params;
+		int depth = 1;
+
+		rparen = scan_next(p);
+		while (!eof_token(rparen)) {
+			if (match_op(rparen, '('))
+				depth++;
+			else if (match_op(rparen, ')') && !--depth)
+				break;
+			p = &rparen->next;
+			rparen = scan_next(p);
+		}
+		if (!match_op(rparen, ')')) {
+			sparse_error(rparen->pos, "expected ')'");
+			token->next = rparen;
+			return false;
+		}
+		after_rparen = rparen->next;
+		*p = &eof_token_entry;
+	}
+
+	param_p = &params;
+	while (!eof_token(next = scan_next(param_p))) {
+		if (token_type(next) == TOKEN_IDENT) {
+			struct ident *id;
+			struct position id_pos;
+			enum embed_param_kind kind;
+
+			if (!expand_one_symbol(param_p))
+				continue;
+			next = *param_p;
+			if (token_type(next) != TOKEN_IDENT) {
+				supported = 0;
+				param_p = &next->next;
+				continue;
+			}
+			id = next->ident;
+			id_pos = next->pos;
+			if (match_op(next->next, ':') &&
+			    match_op(next->next->next, ':')) {
+				struct token *attr = next->next->next->next;
+
+				if (token_type(attr) != TOKEN_IDENT) {
+					sparse_error(attr->pos,
+						     "identifier expected");
+					token->next = after_rparen;
+					return false;
+				}
+				supported = 0;
+				next = attr->next;
+				if (match_op(next, '(')) {
+					int depth = 1;
+
+					next = next->next;
+					while (!eof_token(next) && depth > 0) {
+						if (match_op(next, '('))
+							depth++;
+						else if (match_op(next, ')'))
+							depth--;
+						next = next->next;
+					}
+				}
+				param_p = &next;
+				continue;
+			}
+
+			kind = classify_embed_param(id);
+			if (kind != EMBED_PARAM_NONE) {
+				struct token *pstart, *pend = NULL;
+				int depth = 1;
+
+				if (seen_params & kind) {
+					sparse_error(
+						id_pos,
+						"duplicate embed parameter '%s'",
+						show_ident(id));
+					token->next = after_rparen;
+					return false;
+				}
+				seen_params |= kind;
+
+				next = scan_next(&next->next);
+				if (!match_op(next, '(')) {
+					sparse_error(
+						id_pos,
+						"expected '(' after '%s'",
+						show_ident(id));
+					token->next = after_rparen;
+					return false;
+				}
+				pstart = next->next;
+				next = next->next;
+				while (!eof_token(next) && depth > 0) {
+					if (match_op(next, '('))
+						depth++;
+					else if (match_op(next, ')') &&
+						 !--depth)
+						break;
+					pend = next;
+					next = next->next;
+				}
+				if (!match_op(next, ')')) {
+					sparse_error(id_pos, "expected ')'");
+					token->next = after_rparen;
+					return false;
+				}
+				next = next->next;
+				if (kind == EMBED_PARAM_LIMIT) {
+					if (!eval_embed_limit_expr(
+						    id_pos, pstart, pend,
+						    &limit_val)) {
+						token->next = after_rparen;
+						return false;
+					}
+					has_limit = 1;
+				}
+				param_p = &next;
+				continue;
+			}
+
+			/* Unknown non-prefixed parameter */
+			supported = 0;
+			next = scan_next(&next->next);
+			if (match_op(next, '(')) {
+				int depth = 1;
+
+				next = next->next;
+				while (!eof_token(next) && depth > 0) {
+					if (match_op(next, '('))
+						depth++;
+					else if (match_op(next, ')'))
+						depth--;
+					next = next->next;
+				}
+			}
+			param_p = &next;
+			continue;
+		}
+		supported = 0;
+		param_p = &next->next;
+	}
+
+	if (!supported) {
+		val = 0;
+	} else if (!check_has_embed(stream, filename, expect, has_limit,
+				    limit_val, &filesize)) {
+		val = 0;
+	} else if (filesize == 0 || (has_limit && limit_val == 0)) {
+		val = 2;
+	} else {
+		val = 1;
+	}
+
+	replace_with_int(token, val);
+	token->next = after_rparen;
+	return true;
+
+err:
+	token->next = skip_balanced_parens(lparen);
+	return false;
+}
+
 static int free_preprocessor_line(struct token *token)
 {
 	while (token_type(token) != TOKEN_EOF) {
@@ -1933,6 +2363,12 @@ static int expression_value(struct token **where)
 				replace_with_bool(p, 0);
 				break;
 			}
+			if (p->ident == &__has_embed_ident) {
+				if (parse_has_embed(list))
+					continue;
+				replace_with_bool(p, 0);
+				break;
+			}
 			if (!expand_one_symbol(list))
 				continue;
 			if (token_type(p) != TOKEN_IDENT)
@@ -2598,6 +3034,7 @@ static void init_preprocessor(void)
 		{ "__has_feature", NULL, expand_has_feature },
 		{ "__has_include", NULL, NULL },
 		{ "__has_include_next", NULL, NULL },
+		{ "__has_embed", NULL, NULL },
 	};
 
 	for (i = 0; i < ARRAY_SIZE(normal); i++) {
diff --git a/predefine.c b/predefine.c
index d6ab0785abad..5639fc6862e3 100644
--- a/predefine.c
+++ b/predefine.c
@@ -123,6 +123,9 @@ void predefined_macros(void)
 		predefine("__STDC_NO_COMPLEX__", 1, "1");
 		predefine("__STDC_NO_THREADS__", 1, "1");
 	}
+	predefine("__STDC_EMBED_NOT_FOUND__", 1, "0");
+	predefine("__STDC_EMBED_FOUND__", 1, "1");
+	predefine("__STDC_EMBED_EMPTY__", 1, "2");
 
 	predefine("__CHAR_BIT__", 1, "%d", bits_in_char);
 	if (funsigned_char)
diff --git a/validation/preprocessor/c23-has-embed.c b/validation/preprocessor/c23-has-embed.c
new file mode 100644
index 000000000000..8ae7bb1d081f
--- /dev/null
+++ b/validation/preprocessor/c23-has-embed.c
@@ -0,0 +1,69 @@
+#if defined(__has_embed)
+int has_macro = 1;
+#endif
+
+#if __has_embed("options.h") == __STDC_EMBED_FOUND__
+int found_options = 1;
+#endif
+
+#if __has_embed("options.h" limit(0)) == __STDC_EMBED_EMPTY__
+int limit_zero_empty = 1;
+#endif
+
+#if __has_embed("options.h" __limit__(0)) == __STDC_EMBED_EMPTY__
+int dunder_limit_zero_empty = 1;
+#endif
+
+#if __has_embed("options.h" limit(5)) == __STDC_EMBED_FOUND__
+int limit_five_found = 1;
+#endif
+
+#if __has_embed("nonexistent_embed_12345.bin") == __STDC_EMBED_NOT_FOUND__
+int not_found = 1;
+#endif
+
+#if __has_embed("options.h" prefix(1) suffix(2)) == __STDC_EMBED_FOUND__ && \
+    __has_embed("options.h" __prefix__(1) __suffix__(2) __if_empty__(3)) == \
+	__STDC_EMBED_FOUND__
+int standard_params = 1;
+#endif
+
+#if __has_embed("options.h" unknown_vendor::param(1)) == \
+	__STDC_EMBED_NOT_FOUND__
+int unknown_param = 1;
+#endif
+
+#define HAS_EMB __has_embed
+#if HAS_EMB("options.h") == __STDC_EMBED_FOUND__
+int aliased_has_embed = 1;
+#endif
+
+#if __has_embed("/dev/null") == __STDC_EMBED_EMPTY__
+int dev_null_empty = 1;
+#endif
+
+#if __has_embed("options.h" limit(1ULL << 63)) == __STDC_EMBED_FOUND__ && \
+    __has_embed("options.h" limit(0xffffffffffffffffULL)) == \
+	__STDC_EMBED_FOUND__
+int large_unsigned_limit = 1;
+#endif
+
+/*
+ * check-name: c23-has-embed
+ * check-command: sparse -std=c23 -E -I.. $file
+ *
+ * check-output-start
+
+int has_macro = 1;
+int found_options = 1;
+int limit_zero_empty = 1;
+int dunder_limit_zero_empty = 1;
+int limit_five_found = 1;
+int not_found = 1;
+int standard_params = 1;
+int unknown_param = 1;
+int aliased_has_embed = 1;
+int dev_null_empty = 1;
+int large_unsigned_limit = 1;
+ * check-output-end
+ */

  parent reply	other threads:[~2026-11-09 20:33 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 ` [PATCH 50/73] c23: Support constexpr object specifier Bart Van Assche
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 ` Bart Van Assche [this message]
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-56-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