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 14/73] inline: Copy union cast types and VLA bounds when inlining
Date: Mon,  9 Nov 2026 12:32:00 -0800	[thread overview]
Message-ID: <20261109203259.362845-15-bvanassche@acm.org> (raw)
In-Reply-To: <20261109203259.362845-1-bvanassche@acm.org>

Duplicate union EXPR_CAST target symbols and clone variable-length array
SYM_ARRAY chains in inline.c so that union casts and VLA dimension
expressions inside inlined functions do not share or mutate symbols
across call sites.

Add regression tests in validation/eval-expand-inline-fixes.c and
validation/eval-ptr-incomplete-and-inline.c.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 inline.c                                    | 101 ++++++++++++++++++--
 validation/eval-expand-inline-fixes.c       |  49 ++++++++++
 validation/eval-ptr-incomplete-and-inline.c |  70 ++++++++++++++
 3 files changed, 213 insertions(+), 7 deletions(-)
 create mode 100644 validation/eval-expand-inline-fixes.c
 create mode 100644 validation/eval-ptr-incomplete-and-inline.c

diff --git a/inline.c b/inline.c
index a7ab73d37492..1948bea52ffb 100644
--- a/inline.c
+++ b/inline.c
@@ -35,6 +35,7 @@
 #include "evaluate.h"
 
 static void copy_statement(struct statement *src, struct statement *dst);
+static struct expression *copy_expression(struct expression *expr);
 
 static struct expression * dup_expression(struct expression *expr)
 {
@@ -63,6 +64,64 @@ static struct symbol *copy_symbol(struct position pos, struct symbol *sym)
 	return sym->replace;
 }
 
+static struct symbol *copy_type(struct symbol *type)
+{
+	if (!type)
+		return NULL;
+	switch (type->type) {
+	case SYM_NODE: {
+		struct symbol *base = copy_type(type->ctype.base_type);
+		struct symbol *n;
+		if (base == type->ctype.base_type)
+			return type;
+		n = alloc_symbol(type->pos, SYM_NODE);
+		*n = *type;
+		n->ctype.base_type = base;
+		if (base && base->bit_size < 0)
+			n->examined = 0;
+		return n;
+	}
+	case SYM_ARRAY: {
+		struct symbol *elem = copy_type(type->ctype.base_type);
+		struct expression *size = copy_expression(type->array_size);
+		struct symbol *arr;
+		if (elem == type->ctype.base_type && size == type->array_size && !type->array_size)
+			return type;
+		arr = alloc_symbol(type->pos, SYM_ARRAY);
+		*arr = *type;
+		arr->ctype.base_type = elem;
+		arr->array_size = size;
+		if (type->array_size || elem != type->ctype.base_type)
+			arr->examined = 0;
+		return arr;
+	}
+	case SYM_PTR: {
+		struct symbol *base = copy_type(type->ctype.base_type);
+		struct symbol *ptr;
+		if (base == type->ctype.base_type)
+			return type;
+		ptr = alloc_symbol(type->pos, SYM_PTR);
+		*ptr = *type;
+		ptr->ctype.base_type = base;
+		return ptr;
+	}
+	case SYM_TYPEOF:
+	case SYM_TYPEOF_UNQUAL: {
+		struct expression *init = copy_expression(type->initializer);
+		struct symbol *t;
+		if (init == type->initializer)
+			return type;
+		t = alloc_symbol(type->pos, type->type);
+		*t = *type;
+		t->initializer = init;
+		t->examined = 0;
+		return t;
+	}
+	default:
+		return type;
+	}
+}
+
 static struct symbol_list *copy_symbol_list(struct symbol_list *src)
 {
 	struct symbol_list *dst = NULL;
@@ -155,11 +214,19 @@ static struct expression * copy_expression(struct expression *expr)
 
 	/* Cast/sizeof/__alignof__ */
 	case EXPR_CAST:
-		if (!expr->cast_expression)
-			return NULL;
-		if (expr->cast_expression->type == EXPR_INITIALIZER) {
+		if (!expr->cast_expression) {
+			struct symbol *cast_type = copy_type(expr->cast_type);
+			if (cast_type != expr->cast_type) {
+				expr = dup_expression(expr);
+				expr->cast_type = cast_type;
+			}
+			return expr;
+		}
+		if (expr->cast_expression->type == EXPR_INITIALIZER ||
+		    (expr->cast_type && expr->cast_type->ctype.base_type &&
+		     expr->cast_type->ctype.base_type->type == SYM_UNION)) {
 			struct expression *cast = expr->cast_expression;
-			struct symbol *sym = expr->cast_type;
+			struct symbol *sym = copy_type(expr->cast_type);
 			expr = dup_expression(expr);
 			expr->cast_expression = copy_expression(cast);
 			expr->cast_type = alloc_symbol(sym->pos, sym->type);
@@ -172,10 +239,12 @@ static struct expression * copy_expression(struct expression *expr)
 	case EXPR_PTRSIZEOF:
 	case EXPR_ALIGNOF: {
 		struct expression *cast = copy_expression(expr->cast_expression);
-		if (cast == expr->cast_expression)
+		struct symbol *cast_type = copy_type(expr->cast_type);
+		if (cast == expr->cast_expression && cast_type == expr->cast_type)
 			break;
 		expr = dup_expression(expr);
 		expr->cast_expression = cast;
+		expr->cast_type = cast_type;
 		break;
 	}
 
@@ -343,8 +412,10 @@ static struct statement *copy_one_statement(struct statement *stmt)
 		newstmt->declaration = NULL;
 		FOR_EACH_PTR(stmt->declaration, sym) {
 			struct symbol *newsym = copy_symbol(stmt->pos, sym);
-			if (newsym != sym)
+			if (newsym != sym) {
+				newsym->ctype.base_type = copy_type(newsym->ctype.base_type);
 				newsym->initializer = copy_expression(sym->initializer);
+			}
 			add_symbol(&newstmt->declaration, newsym);
 		} END_FOR_EACH_PTR(sym);
 		stmt = newstmt;
@@ -424,10 +495,20 @@ static struct statement *copy_one_statement(struct statement *stmt)
 		break;		
 	}
 	case STMT_ITERATOR: {
+		struct symbol_list *old_syms = stmt->iterator_syms;
+		struct symbol *sym;
 		stmt = dup_statement(stmt);
 		stmt->iterator_break = copy_symbol(stmt->pos, stmt->iterator_break);
 		stmt->iterator_continue = copy_symbol(stmt->pos, stmt->iterator_continue);
-		stmt->iterator_syms = copy_symbol_list(stmt->iterator_syms);
+		stmt->iterator_syms = NULL;
+		FOR_EACH_PTR(old_syms, sym) {
+			struct symbol *newsym = copy_symbol(stmt->pos, sym);
+			if (newsym != sym) {
+				newsym->ctype.base_type = copy_type(newsym->ctype.base_type);
+				newsym->initializer = copy_expression(sym->initializer);
+			}
+			add_symbol(&stmt->iterator_syms, newsym);
+		} END_FOR_EACH_PTR(sym);
 
 		stmt->iterator_pre_statement = copy_one_statement(stmt->iterator_pre_statement);
 		stmt->iterator_pre_condition = copy_expression(stmt->iterator_pre_condition);
@@ -542,6 +623,8 @@ int inline_function(struct expression *expr, struct symbol *sym)
 
 		if (name) {
 			*a = *name;
+			a->bb_target = NULL;
+			a->pseudo = NULL;
 			set_replace(name, a);
 			add_symbol(&fn_symbol_list, a);
 			a->initializer = arg;
@@ -557,6 +640,10 @@ int inline_function(struct expression *expr, struct symbol *sym)
 	} END_FOR_EACH_PTR(arg);
 	FINISH_PTR_LIST(name);
 
+	FOR_EACH_PTR(arg_decl, name) {
+		name->ctype.base_type = copy_type(name->ctype.base_type);
+	} END_FOR_EACH_PTR(name);
+
 	copy_statement(fn->inline_stmt, stmt);
 
 	if (arg_decl) {
diff --git a/validation/eval-expand-inline-fixes.c b/validation/eval-expand-inline-fixes.c
new file mode 100644
index 000000000000..95600c32ec93
--- /dev/null
+++ b/validation/eval-expand-inline-fixes.c
@@ -0,0 +1,49 @@
+/* 1. _Generic array/function decay and qualifier stripping */
+extern int arr[4];
+extern int fn(void);
+const int ci = 0;
+
+_Static_assert(_Generic(arr, int *: 1, default: 0) == 1, "array decay");
+_Static_assert(_Generic(fn, int (*)(void): 1, default: 0) == 1, "func decay");
+_Static_assert(_Generic(ci, int: 1, const int: 0) == 1, "qual strip");
+
+/* 2. Inlining function with cleanup attribute and _Static_assert */
+void cl(int *p);
+
+static inline int inl_cleanup(int v)
+{
+	_Static_assert(sizeof(v) == sizeof(int), "int size");
+	int x __attribute__((cleanup(cl))) = v;
+	return x + 1;
+}
+
+int call_inl(int v)
+{
+	return inl_cleanup(v);
+}
+
+/* 3. Conditional operator preserving noderef qualifier */
+#define __noderef __attribute__((noderef))
+void cond_noderef(void *vp, int __noderef *p)
+{
+	int *q = (1 ? vp : p);
+	(void)q;
+}
+
+/* 4. Signed division overflow in constant expression */
+int div_ovf(void)
+{
+	return (-9223372036854775807LL - 1LL) / -1LL;
+}
+
+/*
+ * check-name: eval-expand-inline-fixes
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+eval-expand-inline-fixes.c:29:21: warning: incorrect type in initializer (different modifiers)
+eval-expand-inline-fixes.c:29:21:    expected int *q
+eval-expand-inline-fixes.c:29:21:    got void [noderef] *
+eval-expand-inline-fixes.c:36:47: warning: constant integer operation overflow
+ * check-error-end
+ */
diff --git a/validation/eval-ptr-incomplete-and-inline.c b/validation/eval-ptr-incomplete-and-inline.c
new file mode 100644
index 000000000000..fb1c7b71496b
--- /dev/null
+++ b/validation/eval-ptr-incomplete-and-inline.c
@@ -0,0 +1,70 @@
+/* 1. Subtraction of pointers to incomplete type */
+extern int arr[];
+static int test_sub(int (*p)[], int (*q)[])
+{
+	return p - q;
+}
+
+/* 2. _Alignof on incomplete type (void) */
+static unsigned long test_align(void)
+{
+	return _Alignof(void);
+}
+
+/* 3. Float <-> unsigned integer constant folding in initializers */
+static unsigned int u = (unsigned int)3.8f;
+static float f = 4000000000U;
+
+/* 4. Unsigned pointer arithmetic */
+static char *test_ptr_add(char *p)
+{
+	return p + 0x80000000U;
+}
+
+/* 5. Named asm constraint */
+static int test_asm_named(int val)
+{
+	int out;
+	asm("mov %0, %[in]" : "=r"(out) : [in] "r"(val));
+	return out;
+}
+
+/* 6. Inlining with local array and union cast */
+union U { int x; float y; };
+static inline int inl_func(int v)
+{
+	int a[4] = { v, 1, 2, 3 };
+	union U u = (union U)v;
+	return a[0] + u.x;
+}
+static int call_inl(int x, int y)
+{
+	return inl_func(x) + inl_func(y);
+}
+
+/* 7. Inlining multi-dimensional VLAs, VLA pointers, and typeof(VLA) */
+static inline unsigned long inl_vla(int n, int m)
+{
+	int mat[n][m];
+	int (*p)[m] = mat;
+	typeof(mat) mat2;
+
+	for (int row[n], k = 0; k < 1; k++)
+		row[0] = n;
+	return sizeof(mat) + sizeof(*p) + sizeof(mat2) + sizeof(int [n][m]);
+}
+
+static unsigned long call_inl_vla(int a, int b, int c, int d)
+{
+	return inl_vla(a, b) + inl_vla(c, d);
+}
+
+/*
+ * check-name: eval-ptr-incomplete-and-inline
+ * check-command: sparse -Wno-vla $file
+ *
+ * check-error-start
+eval-ptr-incomplete-and-inline.c:5:18: error: subtraction of pointers to incomplete type
+eval-ptr-incomplete-and-inline.c:11:16: error: cannot size alignment of incomplete type
+ * check-error-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 ` Bart Van Assche [this message]
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 ` [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-15-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