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 47/73] c23: Support variadic functions without named parameters
Date: Mon,  9 Nov 2026 12:32:33 -0800	[thread overview]
Message-ID: <20261109203259.362845-48-bvanassche@acm.org> (raw)
In-Reply-To: <20261109203259.362845-1-bvanassche@acm.org>

Allow variadic function prototypes and definitions of the form 'f(...)'
without a preceding named parameter and support single-argument va_start
in C23 mode per ISO C23 6.7.6.3 and 7.16.1.4:
- Replace the predefined 2-argument __builtin_va_start macro in lib.c
  with builtin handlers for __builtin_va_start and
  __builtin_c23_va_start in builtin.c so <stdarg.h> works with both
  va_start(ap) and va_start(ap, ...).
- Validate that va_start appears inside a variadic function on a
  modifiable va_list lvalue, leave extra C23 va_start arguments
  unevaluated, and lower va_start to initialize ap.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 builtin.c                          | 77 ++++++++++++++++++++++++++++++
 lib.c                              |  1 -
 parse.c                            |  5 +-
 validation/c11-va-start-error.c    | 20 ++++++++
 validation/c23-variadic-error.c    | 41 ++++++++++++++++
 validation/c23-variadic-no-named.c | 47 ++++++++++++++++++
 validation/linear/c23-va-start.c   | 22 +++++++++
 7 files changed, 210 insertions(+), 3 deletions(-)
 create mode 100644 validation/c11-va-start-error.c
 create mode 100644 validation/c23-variadic-error.c
 create mode 100644 validation/c23-variadic-no-named.c
 create mode 100644 validation/linear/c23-va-start.c

diff --git a/builtin.c b/builtin.c
index 78a971ce4bb5..0be03c51aedd 100644
--- a/builtin.c
+++ b/builtin.c
@@ -638,6 +638,81 @@ static struct symbol_op object_size_op = {
 	.expand = expand_object_size,
 };
 
+static int args_va_start(struct expression *expr)
+{
+	struct symbol *sym = expr->fn->ctype;
+	bool is_c23 = !strcmp(show_ident(sym->ident), "__builtin_c23_va_start");
+	int n = expression_list_size(expr->args);
+	struct expression *arg;
+	int i = 0;
+
+	if (n < 1 || (!is_c23 && standard < STANDARD_C23 && n < 2)) {
+		expression_error(expr, "not enough arguments for %s",
+				 show_ident(sym->ident));
+		return 0;
+	}
+	if (!is_c23 && n > 2) {
+		expression_error(expr, "too many arguments for %s",
+				 show_ident(sym->ident));
+		return 0;
+	}
+	FOR_EACH_PTR(expr->args, arg) {
+		if (i == 1 && !is_c23) {
+			if (!evaluate_expression(arg))
+				return 0;
+		}
+		i++;
+	} END_FOR_EACH_PTR(arg);
+	return 1;
+}
+
+static int evaluate_va_start(struct expression *expr)
+{
+	struct expression *ap = first_expression(expr->args);
+	struct symbol *fntype = current_fn ? current_fn->ctype.base_type : NULL;
+	struct expression *assign, *rhs;
+
+	if (current_fn && current_fn->type == SYM_FN)
+		fntype = current_fn;
+	if (!fntype || !fntype->variadic) {
+		expression_error(expr, "'va_start' used in function with fixed args");
+		return 1;
+	}
+
+	if (!evaluate_expression(ap) || !valid_type(ap->ctype))
+		return 1;
+	{
+		struct symbol *atype = ap->ctype;
+		while (atype && atype->type == SYM_NODE)
+			atype = atype->ctype.base_type;
+		if (atype != &ptr_ctype) {
+			expression_error(expr, "incorrect type in 'va_start'");
+			return 1;
+		}
+	}
+
+	rhs = alloc_expression(expr->pos, EXPR_VALUE);
+	rhs->ctype = &ptr_ctype;
+	rhs->value = 0;
+
+	assign = alloc_expression(expr->pos, EXPR_ASSIGNMENT);
+	assign->op = '=';
+	assign->left = ap;
+	assign->right = rhs;
+	evaluate_expression(assign);
+
+	expr->type = EXPR_CAST;
+	expr->cast_type = &void_ctype;
+	expr->cast_expression = assign;
+	expr->ctype = &void_ctype;
+	return 1;
+}
+
+static struct symbol_op va_start_op = {
+	.args = args_va_start,
+	.evaluate = evaluate_va_start,
+};
+
 static int evaluate_strlen(struct expression *expr)
 {
 	struct expression *arg = first_expression(expr->args);
@@ -876,7 +951,9 @@ static const struct builtin_fn builtins_common[] = {
 	{ "__builtin_usub_overflow", &bool_ctype, 0, { &uint_ctype, &uint_ctype, &uint_ptr_ctype }},
 	{ "__builtin_usubl_overflow", &bool_ctype, 0, { &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype }},
 	{ "__builtin_usubll_overflow", &bool_ctype, 0, { &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype }},
+	{ "__builtin_c23_va_start", &void_ctype, 1, .op = &va_start_op },
 	{ "__builtin_va_arg_pack_len", size_t_ctype, 0 },
+	{ "__builtin_va_start", &void_ctype, 1, .op = &va_start_op },
 	{ "__builtin_vprintf", &int_ctype, 0, { &const_string_ctype, va_list_ctype }},
 	{ "__builtin_vsnprintf", &int_ctype, 0, { &string_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }},
 	{ "__builtin_vsprintf", &int_ctype, 0, { &string_ctype, &const_string_ctype, va_list_ctype }},
diff --git a/lib.c b/lib.c
index 6670e3b46657..3c41c41855c4 100644
--- a/lib.c
+++ b/lib.c
@@ -299,7 +299,6 @@ static void create_builtin_stream(void)
 	add_pre_buffer("#add_system \"%s/include-fixed\"\n", gcc_base_dir);
 
 	add_pre_buffer("#define __builtin_stdarg_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
-	add_pre_buffer("#define __builtin_va_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
 	add_pre_buffer("#define __builtin_ms_va_start(a,b) ((a) = (__builtin_ms_va_list)(&(b)))\n");
 	add_pre_buffer("#define __builtin_va_arg(arg,type)  ({ type __va_arg_ret = *(type *)(arg); arg += sizeof(type); __va_arg_ret; })\n");
 	add_pre_buffer("#define __builtin_va_alist (*(void *)0)\n");
diff --git a/parse.c b/parse.c
index 6af5bfba2392..78ad5f09e2ad 100644
--- a/parse.c
+++ b/parse.c
@@ -1926,8 +1926,9 @@ static enum kind which_func(struct token *token,
 	}
 
 	if (next->special == SPECIAL_ELLIPSIS) {
-		warning(next->pos,
-			"variadic functions must have one named argument");
+		if (standard < STANDARD_C23)
+			warning(next->pos,
+				"variadic functions must have one named argument");
 		return Proto;
 	}
 
diff --git a/validation/c11-va-start-error.c b/validation/c11-va-start-error.c
new file mode 100644
index 000000000000..334ad0cd48ad
--- /dev/null
+++ b/validation/c11-va-start-error.c
@@ -0,0 +1,20 @@
+#include <stdarg.h>
+
+static void f(int n, ...)
+{
+	va_list ap;
+	(void)n;
+	va_start(ap);
+	__builtin_va_start(ap);
+}
+
+/*
+ * check-name: c11-va-start-error
+ * check-command: sparse -std=c11 $file
+ *
+ * check-error-start
+c11-va-start-error.c:7:9: error: macro "va_start" requires 2 arguments, but only 1 given
+c11-va-start-error.c:7:9: error: undefined identifier 'va_start'
+c11-va-start-error.c:8:27: error: not enough arguments for __builtin_va_start
+ * check-error-end
+ */
diff --git a/validation/c23-variadic-error.c b/validation/c23-variadic-error.c
new file mode 100644
index 000000000000..85032625fff3
--- /dev/null
+++ b/validation/c23-variadic-error.c
@@ -0,0 +1,41 @@
+#include <stdarg.h>
+
+static const va_list const_ap;
+
+static void fixed_fn(int x)
+{
+	va_list ap;
+	(void)x;
+	va_start(ap);
+}
+
+static void variadic_errors(...)
+{
+	int not_ap = 0;
+	void *void_ptr = nullptr;
+	va_list ap;
+
+	__builtin_va_start();
+	__builtin_va_start(ap, 1, 2);
+	va_start(not_ap);
+	va_start(void_ptr);
+	va_start(const_ap);
+	(void)(va_start(ap) + 1);
+}
+
+/*
+ * check-name: c23-variadic-error
+ * check-command: sparse -std=c23 $file
+ *
+ * check-error-start
+c23-variadic-error.c:9:9: error: 'va_start' used in function with fixed args
+c23-variadic-error.c:18:27: error: not enough arguments for __builtin_va_start
+c23-variadic-error.c:19:27: error: too many arguments for __builtin_va_start
+c23-variadic-error.c:20:9: error: incorrect type in 'va_start'
+c23-variadic-error.c:21:9: error: incorrect type in 'va_start'
+c23-variadic-error.c:22:9: error: assignment to const expression
+c23-variadic-error.c:23:29: error: incompatible types for operation (+):
+c23-variadic-error.c:23:29:    void
+c23-variadic-error.c:23:29:    int
+ * check-error-end
+ */
diff --git a/validation/c23-variadic-no-named.c b/validation/c23-variadic-no-named.c
new file mode 100644
index 000000000000..35e38f7ae177
--- /dev/null
+++ b/validation/c23-variadic-no-named.c
@@ -0,0 +1,47 @@
+#include <stdarg.h>
+
+static void v1(...)
+{
+}
+
+static int sum_two(...)
+{
+	va_list ap, copy;
+	int a, b;
+
+	va_start(ap);
+	va_copy(copy, ap);
+	a = va_arg(ap, int);
+	b = va_arg(ap, int);
+	va_end(copy);
+	va_end(ap);
+	return a + b;
+}
+
+static int sum_after_n(int n, ...)
+{
+	va_list ap;
+	int a;
+
+	va_start(ap, n);
+	va_end(ap);
+	va_start(ap, undeclared_ignored_token, 1, 2);
+	a = va_arg(ap, int);
+	va_end(ap);
+	return n + a;
+}
+
+static int test(void)
+{
+	int (*fp)(...) = sum_two;
+
+	v1();
+	v1(1);
+	v1(1, 2, "three");
+	return fp(10, 20) + sum_after_n(5, 7);
+}
+
+/*
+ * check-name: c23-variadic-no-named
+ * check-command: sparse -std=c23 -pedantic $file
+ */
diff --git a/validation/linear/c23-va-start.c b/validation/linear/c23-va-start.c
new file mode 100644
index 000000000000..2d5b11d58aa4
--- /dev/null
+++ b/validation/linear/c23-va-start.c
@@ -0,0 +1,22 @@
+#include <stdarg.h>
+
+int first_arg(...);
+int first_arg(...)
+{
+	va_list ap;
+	int v;
+
+	va_start(ap);
+	v = va_arg(ap, int);
+	va_end(ap);
+	return v;
+}
+
+/*
+ * check-name: linear-c23-va-start
+ * check-command: test-linearize -m64 -std=c23 -Wuninitialized $file
+ *
+ * check-output-ignore
+ * check-output-excludes: __builtin_va_start
+ * check-output-excludes: __builtin_c23_va_start
+ */

  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 ` Bart Van Assche [this message]
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 ` [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-48-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