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

Implement EXPR_HAS_ATTRIBUTE to evaluate __builtin_has_attribute queries
at compile time across symbols, expressions, and anonymous type names:
- Distinguish __attribute__((const)) and [[unsequenced]] from pure via
  MOD_FCONST (0x80000000UL) and report const on const-qualified object
  declarations without conflating pure functions or pointer targets.
- Define MOD_NONSTRING (0x40000000) for the nonstring attribute instead of
  ignoring it, and support nonstring in __builtin_has_attribute() so Linux
  kernel strtomem_pad() static assertions succeed.
- Match GCC packed+aligned struct, union, bitfield, typedef, and _Alignas
  ABI layout via explicit_alignment on decl_state and symbol, and prevent
  typedef MOD_ALIGNED, MOD_WEAK, and MOD_NONSTRING modifiers from leaking
  onto pointers.
- Support bitwise SYM_RESTRICT types, struct member attribute queries
  without parent leakage, redeclaration attribute inheritance, and
  TOKEN_IDENT validation.

Add validation/struct-packed-aligned-layout.c,
validation/builtin-has-attribute-more.c,
validation/builtin-has-attribute-nonstring.c, and
validation/builtin-has-attribute-err.c.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 ast-inspect.c                                |   1 +
 builtin.c                                    |   1 +
 c2xml.c                                      |  44 +++---
 dissect.c                                    |   4 +
 evaluate.c                                   |  36 ++++-
 expand.c                                     | 121 +++++++++++++-
 expression.c                                 |  82 ++++++++++
 expression.h                                 |   8 +
 gcc-attr-list.h                              |   2 -
 ident-list.h                                 |   1 +
 inline.c                                     |   6 +
 parse.c                                      | 108 ++++++++++---
 show-parse.c                                 |   5 +
 simplify.c                                   |   2 +-
 symbol.c                                     |  30 ++--
 symbol.h                                     |  20 ++-
 validation/builtin-has-attribute-err.c       |  19 +++
 validation/builtin-has-attribute-more.c      | 157 +++++++++++++++++++
 validation/builtin-has-attribute-nonstring.c |  35 +++++
 validation/builtins-kernel.c                 |  31 ++++
 validation/struct-packed-aligned-layout.c    | 135 ++++++++++++++++
 validation/weak-const-no-fold.c              |  15 ++
 22 files changed, 801 insertions(+), 62 deletions(-)
 create mode 100644 validation/builtin-has-attribute-err.c
 create mode 100644 validation/builtin-has-attribute-more.c
 create mode 100644 validation/builtin-has-attribute-nonstring.c
 create mode 100644 validation/struct-packed-aligned-layout.c
 create mode 100644 validation/weak-const-no-fold.c

diff --git a/ast-inspect.c b/ast-inspect.c
index e940a93a411e..578dfca0e1cf 100644
--- a/ast-inspect.c
+++ b/ast-inspect.c
@@ -180,6 +180,7 @@ static const char *expression_type_name(enum expression_type type)
 		[EXPR_FVALUE] = "EXPR_FVALUE",
 		[EXPR_SLICE] = "EXPR_SLICE",
 		[EXPR_OFFSETOF] = "EXPR_OFFSETOF",
+		[EXPR_HAS_ATTRIBUTE] = "EXPR_HAS_ATTRIBUTE",
 	};
 	return expression_type_name[type] ?: "UNKNOWN_EXPRESSION_TYPE";
 }
diff --git a/builtin.c b/builtin.c
index e8ea7721b771..34af6d82b846 100644
--- a/builtin.c
+++ b/builtin.c
@@ -900,6 +900,7 @@ static const struct builtin_fn builtins_common[] = {
 	{ "__builtin_fmal", &ldouble_ctype, 0, { &ldouble_ctype, &ldouble_ctype, &ldouble_ctype }},
 	{ "__builtin_frame_address", &ptr_ctype, 0, { &uint_ctype }},
 	{ "__builtin_free", &void_ctype, 0, { &ptr_ctype }},
+	{ "__builtin_has_attribute", &int_ctype, 1 },
 	{ "__builtin_huge_val", &double_ctype, 0 },
 	{ "__builtin_huge_valf", &float_ctype, 0 },
 	{ "__builtin_huge_vall", &ldouble_ctype, 0 },
diff --git a/c2xml.c b/c2xml.c
index f23ec1a72de4..8abcadcd2320 100644
--- a/c2xml.c
+++ b/c2xml.c
@@ -113,34 +113,34 @@ static void examine_modifiers(struct symbol *sym, xmlNodePtr node)
 			"register",
 			"static",
 			"extern",
+			"toplevel",
+			"tls",
+			"inline",
+			"assigned",
+			"addressable",
 			"const",
 			"volatile",
+			"restrict",
+			"atomic",
 			"signed",
 			"unsigned",
-			"char",
-			"short",
-			"long",
-			"long-long",
-			"typedef",
-			NULL,
-			NULL,
-			NULL,
-			NULL,
-			NULL,
-			"inline",
-			"addressable",
+			"explicitly-signed",
+			"gnu-inline",
+			"user-type",
+			"constexpr",
+			"bitint",
+			"weak",
+			"aligned",
+			"unused",
+			"safe",
+			"pure",
+			"bitwise",
 			"nocast",
 			"noderef",
-			"accessed",
-			"toplevel",
-			"label",
-			"assigned",
-			"type-type",
-			"safe",
-			"user-type",
-			"force",
-			"explicitly-signed",
-			"bitwise"};
+			"noreturn",
+			"ext-visible",
+			"nonstring",
+			"fconst"};
 
 	int i;
 
diff --git a/dissect.c b/dissect.c
index 5d25e02fa1ad..7dd6161a29fa 100644
--- a/dissect.c
+++ b/dissect.c
@@ -483,6 +483,10 @@ again:
 			ret = do_expression(mode, expr->def);
 	}
 
+	break; case EXPR_HAS_ATTRIBUTE:
+		if (expr->has_attr_expr)
+			do_expression(mode, expr->has_attr_expr);
+
 	break; case EXPR_SYMBOL:
 		ret = report_symbol(mode, expr);
 	}
diff --git a/evaluate.c b/evaluate.c
index 1c169ad60382..27bfe1c0fe88 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -2433,7 +2433,8 @@ static struct symbol *evaluate_member_dereference(struct expression *expr)
 	 * The member needs to take on the address space and modifiers of
 	 * the "parent" type.
 	 */
-	member = convert_to_as_mod(member, address_space, mod);
+	member = convert_to_as_mod(member, address_space,
+				    mod & (MOD_PTRINHERIT | MOD_CONSTEXPR));
 	ctype = get_base_type(member);
 
 	if (!lvalue_expression(deref)) {
@@ -4098,6 +4099,39 @@ struct symbol *evaluate_expression(struct expression *expr)
 	case EXPR_GENERIC:
 		return evaluate_generic_selection(expr);
 
+	case EXPR_HAS_ATTRIBUTE:
+		if (expr->has_attr_expr) {
+			struct expression *sub = expr->has_attr_expr;
+			while (sub && sub->type == EXPR_PREOP && sub->op == '(')
+				sub = sub->unop;
+			if (sub && sub->type == EXPR_SYMBOL) {
+				expr->has_attr_decl = true;
+				evaluate_expression(expr->has_attr_expr);
+			} else if (sub && sub->type == EXPR_DEREF) {
+				struct symbol *dctype = evaluate_expression(sub->deref);
+				if (dctype && sub->member) {
+					int offset = 0;
+					examine_symbol_type(dctype);
+					if (dctype->type == SYM_NODE)
+						dctype = dctype->ctype.base_type;
+					if (dctype && (dctype->type == SYM_STRUCT ||
+						       dctype->type == SYM_UNION)) {
+						expr->has_attr_sym = find_identifier(
+							sub->member, dctype->symbol_list, &offset);
+						expr->has_attr_decl = true;
+					}
+				}
+				evaluate_expression(expr->has_attr_expr);
+			} else {
+				evaluate_expression(expr->has_attr_expr);
+			}
+		}
+		if (expr->has_attr_sym)
+			examine_symbol_type(expr->has_attr_sym);
+		expr->ctype = &int_ctype;
+		expr->flags = CEF_SET_ICE;
+		return &int_ctype;
+
 	/* These can not exist as stand-alone expressions */
 	case EXPR_INITIALIZER:
 	case EXPR_IDENTIFIER:
diff --git a/expand.c b/expand.c
index 57d3aeba0f4e..696662ad11ea 100644
--- a/expand.c
+++ b/expand.c
@@ -1011,7 +1011,7 @@ static struct expression *constant_symbol_value(struct symbol *sym, int offset)
 
 	if (offset < 0 || sym->expanding)
 		return NULL;
-	if (sym->ctype.modifiers & MOD_VOLATILE)
+	if (sym->ctype.modifiers & (MOD_VOLATILE | MOD_WEAK))
 		return NULL;
 	if ((sym->ctype.modifiers & MOD_ACCESS) &&
 	    !(sym->ctype.modifiers & MOD_CONSTEXPR))
@@ -1330,7 +1330,7 @@ static int expand_symbol_call(struct expression *expr, int cost)
 	if (ctype->op && ctype->op->expand)
 		return ctype->op->expand(expr, cost);
 
-	if (ctype->ctype.modifiers & MOD_PURE)
+	if (ctype->ctype.modifiers & (MOD_PURE | MOD_FCONST))
 		return cost + 1;
 
 	return SIDE_EFFECTS;
@@ -1502,6 +1502,120 @@ static void verify_nonoverlapping(struct expression_list **list, struct expressi
 	} END_FOR_EACH_PTR(b);
 }
 
+static const char *normalize_attr_name(const char *name)
+{
+	static char buf[64];
+	size_t len;
+	if (!name)
+		return "";
+	len = strlen(name);
+	if (len >= 4 && name[0] == '_' && name[1] == '_' &&
+	    name[len-1] == '_' && name[len-2] == '_') {
+		if (len - 4 < sizeof(buf)) {
+			memcpy(buf, name + 2, len - 4);
+			buf[len - 4] = '\0';
+			return buf;
+		}
+	}
+	return name;
+}
+
+static bool is_fun_attr_name(const char *name)
+{
+	return !strcmp(name, "pure") || !strcmp(name, "reproducible") ||
+	       !strcmp(name, "const") || !strcmp(name, "unsequenced") ||
+	       !strcmp(name, "noreturn") || !strcmp(name, "_Noreturn") ||
+	       !strcmp(name, "gnu_inline");
+}
+
+static bool check_symbol_has_attr(struct symbol *sym, const char *name, bool is_decl)
+{
+	if (!sym || !name)
+		return false;
+
+	if (!strcmp(name, "packed"))
+		return sym->packed != 0;
+	if (!strcmp(name, "aligned"))
+		return (sym->ctype.modifiers & MOD_ALIGNED) != 0;
+	if (!strcmp(name, "weak"))
+		return (sym->ctype.modifiers & MOD_WEAK) != 0;
+	if (!strcmp(name, "pure") || !strcmp(name, "reproducible"))
+		return (sym->ctype.modifiers & MOD_PURE) != 0;
+	if (!strcmp(name, "unsequenced"))
+		return (sym->ctype.modifiers & MOD_FCONST) != 0;
+	if (!strcmp(name, "const")) {
+		if (sym->ctype.modifiers & MOD_FCONST)
+			return true;
+		return is_decl && (sym->ctype.modifiers & MOD_CONST) != 0;
+	}
+	if (!strcmp(name, "noreturn") || !strcmp(name, "_Noreturn"))
+		return (sym->ctype.modifiers & MOD_NORETURN) != 0;
+	if (!strcmp(name, "noderef"))
+		return (sym->ctype.modifiers & MOD_NODEREF) != 0;
+	if (!strcmp(name, "unused") || !strcmp(name, "maybe_unused"))
+		return (sym->ctype.modifiers & MOD_UNUSED) != 0;
+	if (!strcmp(name, "externally_visible"))
+		return (sym->ctype.modifiers & MOD_EXT_VISIBLE) != 0;
+	if (!strcmp(name, "gnu_inline"))
+		return (sym->ctype.modifiers & MOD_GNU_INLINE) != 0;
+	if (!strcmp(name, "address_space"))
+		return sym->ctype.as != NULL;
+	if (!strcmp(name, "bitwise"))
+		return (sym->ctype.modifiers & MOD_BITWISE) != 0 ||
+		       sym->type == SYM_RESTRICT;
+	if (!strcmp(name, "nocast"))
+		return (sym->ctype.modifiers & MOD_NOCAST) != 0;
+	if (!strcmp(name, "transparent_union"))
+		return sym->transparent_union != 0;
+	if (!strcmp(name, "designated_init"))
+		return sym->designated_init != 0;
+	if (!strcmp(name, "nonstring"))
+		return (sym->ctype.modifiers & MOD_NONSTRING) != 0;
+
+	return false;
+}
+
+static int expand_has_attribute(struct expression *expr)
+{
+	struct symbol *sym = expr->has_attr_sym;
+	struct expression *sub = expr->has_attr_expr;
+	struct ident *attr_id = expr->has_attr_ident;
+	const char *attr_name = attr_id ? normalize_attr_name(show_ident(attr_id)) : "";
+	bool has_attr = false;
+
+	if (!sym && sub) {
+		if (sub->type == EXPR_PREOP && sub->op == '*' &&
+		    sub->unop && sub->unop->type == EXPR_SYMBOL)
+			sym = sub->unop->symbol;
+		else if (sub->type == EXPR_SYMBOL)
+			sym = sub->symbol;
+		else if (sub->ctype)
+			sym = sub->ctype;
+	}
+
+	if (sym) {
+		bool is_decl = expr->has_attr_decl;
+		if (sym->type == SYM_NODE && sym->ctype.base_type) {
+			struct symbol *base = sym->ctype.base_type;
+			if (check_symbol_has_attr(sym, attr_name, is_decl))
+				has_attr = true;
+			else if (base->type != SYM_PTR)
+				has_attr = check_symbol_has_attr(base, attr_name, false);
+			else if (base->ctype.base_type &&
+				 base->ctype.base_type->type == SYM_FN &&
+				 is_fun_attr_name(attr_name))
+				has_attr = check_symbol_has_attr(base, attr_name, false);
+		} else {
+			has_attr = check_symbol_has_attr(sym, attr_name, is_decl);
+		}
+	}
+
+	expr->type = EXPR_VALUE;
+	expr->value = has_attr ? 1 : 0;
+	expr->taint = 0;
+	return 0;
+}
+
 static int expand_expression(struct expression *expr)
 {
 	if (!expr)
@@ -1577,6 +1691,9 @@ static int expand_expression(struct expression *expr)
 	case EXPR_INDEX:
 		return UNSAFE;
 
+	case EXPR_HAS_ATTRIBUTE:
+		return expand_has_attribute(expr);
+
 	case EXPR_SLICE:
 		return expand_expression(expr->base) + 1;
 
diff --git a/expression.c b/expression.c
index 15b9eca351cb..e6f58df84e39 100644
--- a/expression.c
+++ b/expression.c
@@ -185,6 +185,83 @@ static struct token *builtin_types_compatible_p_expr(struct token *token,
 	return token;
 }
 
+static struct token *builtin_has_attribute_expr(struct token *token,
+						struct expression **tree)
+{
+	struct expression *expr = alloc_expression(token->pos, EXPR_HAS_ATTRIBUTE);
+	struct symbol *type_sym = NULL;
+	struct expression *val_expr = NULL;
+	struct ident *attr_ident = NULL;
+
+	token = token->next;
+	if (!match_op(token, '('))
+		return expect(token, '(', "after __builtin_has_attribute");
+	token = token->next;
+
+	if (lookup_type(token)) {
+		token = typename(token, &type_sym, NULL);
+		check_typename_storage(type_sym);
+	} else {
+		token = assignment_expression(token, &val_expr);
+	}
+
+	if (!match_op(token, ','))
+		return expect(token, ',', "in __builtin_has_attribute");
+	token = token->next;
+
+	if (token_type(token) == TOKEN_IDENT &&
+	    (token->ident == &__attribute_ident ||
+	     token->ident == &__attribute___ident)) {
+		token = token->next;
+		if (match_op(token, '(')) {
+			int depth = 1;
+			token = token->next;
+			while (depth && !eof_token(token)) {
+				if (token_type(token) == TOKEN_IDENT && !attr_ident)
+					attr_ident = token->ident;
+				if (token_type(token) == TOKEN_SPECIAL) {
+					if (token->special == '(')
+						depth++;
+					else if (token->special == ')')
+						depth--;
+				}
+				token = token->next;
+			}
+		}
+		if (!attr_ident)
+			sparse_error(token->pos, "expected attribute name");
+	} else if (token_type(token) == TOKEN_IDENT) {
+		attr_ident = token->ident;
+		token = token->next;
+		if (match_op(token, '(')) {
+			int depth = 1;
+			token = token->next;
+			while (depth && !eof_token(token)) {
+				if (token_type(token) == TOKEN_SPECIAL) {
+					if (token->special == '(')
+						depth++;
+					else if (token->special == ')')
+						depth--;
+				}
+				token = token->next;
+			}
+		}
+	} else {
+		sparse_error(token->pos, "expected attribute name");
+		while (!eof_token(token) && !match_op(token, ')'))
+			token = token->next;
+	}
+
+	token = expect(token, ')', "at end of __builtin_has_attribute");
+
+	expr->has_attr_sym = type_sym;
+	expr->has_attr_expr = val_expr;
+	expr->has_attr_ident = attr_ident;
+	expr->flags = CEF_SET_ICE;
+	*tree = expr;
+	return token;
+}
+
 static struct token *builtin_offsetof_expr(struct token *token,
 					   struct expression **tree)
 {
@@ -765,6 +842,11 @@ struct token *primary_expression(struct token *token, struct expression **tree)
 			break;
 		}
 
+		if (token->ident == &__builtin_has_attribute_ident) {
+			token = builtin_has_attribute_expr(token, &expr);
+			break;
+		}
+
 		sym = lookup_symbol(token->ident, NS_SYMBOL | NS_TYPEDEF);
 
 		if (!sym) {
diff --git a/expression.h b/expression.h
index 83857cffc7dc..747056fed5d4 100644
--- a/expression.h
+++ b/expression.h
@@ -65,6 +65,7 @@ enum expression_type {
 	EXPR_SLICE,
 	EXPR_OFFSETOF,
 	EXPR_GENERIC,
+	EXPR_HAS_ATTRIBUTE,
 };
 
 
@@ -265,6 +266,13 @@ struct expression {
 			struct expression *def;
 			struct type_expression *map;
 		};
+		// EXPR_HAS_ATTRIBUTE
+		struct {
+			struct symbol *has_attr_sym;
+			struct expression *has_attr_expr;
+			struct ident *has_attr_ident;
+			bool has_attr_decl;
+		};
 	};
 };
 
diff --git a/gcc-attr-list.h b/gcc-attr-list.h
index 928ea3889e01..3db28e8cc592 100644
--- a/gcc-attr-list.h
+++ b/gcc-attr-list.h
@@ -127,7 +127,6 @@ GCC_ATTR(noipa)
 GCC_ATTR(nomicromips)
 GCC_ATTR(nomips16)
 GCC_ATTR(nonnull)
-GCC_ATTR(nonstring)
 GCC_ATTR(noplt)
 GCC_ATTR(nosave_low_regs)
 GCC_ATTR(not_nested)
@@ -204,6 +203,5 @@ GCC_ATTR(warn_if_not_aligned)
 GCC_ATTR(warn_unused)
 GCC_ATTR(warn_unused_result)
 GCC_ATTR(warning)
-GCC_ATTR(weak)
 GCC_ATTR(weakref)
 GCC_ATTR(zda)
diff --git a/ident-list.h b/ident-list.h
index 4427527580ee..c4a0179ca523 100644
--- a/ident-list.h
+++ b/ident-list.h
@@ -66,6 +66,7 @@ IDENT_RESERVED(__alignof__);
 IDENT_RESERVED(__sizeof_ptr__);
 IDENT_RESERVED(__builtin_types_compatible_p);
 IDENT_RESERVED(__builtin_offsetof);
+IDENT(__builtin_has_attribute);
 IDENT_RESERVED(__label__);
 IDENT(gnu);
 IDENT(__gnu__);
diff --git a/inline.c b/inline.c
index d3f27952138b..c857cd953702 100644
--- a/inline.c
+++ b/inline.c
@@ -363,6 +363,12 @@ static struct expression * copy_expression(struct expression *expr)
 		break;
 	}
 
+	case EXPR_HAS_ATTRIBUTE:
+		expr = dup_expression(expr);
+		if (expr->has_attr_expr)
+			expr->has_attr_expr = copy_expression(expr->has_attr_expr);
+		break;
+
 	default:
 		warning(expr->pos, "trying to copy expression type %d", expr->type);
 	}
diff --git a/parse.c b/parse.c
index 3235274e6659..7010d412fdab 100644
--- a/parse.c
+++ b/parse.c
@@ -587,6 +587,8 @@ static struct init_keyword {
 	D("unused",		&attr_mod_op,		.mods = MOD_UNUSED),
 	D("maybe_unused",	&attr_mod_op,		.mods = MOD_UNUSED),
 	D("externally_visible",	&attr_mod_op,		.mods = MOD_EXT_VISIBLE),
+	D("weak",		&attr_mod_op,		.mods = MOD_WEAK),
+	D("nonstring",		&attr_mod_op,		.mods = MOD_NONSTRING),
 	D("force",		&attr_force_op),
 	D("bitwise",		&attr_bitwise_op,	.mods = MOD_BITWISE),
 	D("address_space",	&address_space_op),
@@ -595,11 +597,11 @@ static struct init_keyword {
 	D("noreturn",		&attr_fun_op,		.mods = MOD_NORETURN),
 	D("_Noreturn",		&attr_fun_op,		.mods = MOD_NORETURN),
 	D("pure",		&attr_fun_op,		.mods = MOD_PURE),
-	A("const",		&attr_fun_op,		.mods = MOD_PURE),
+	A("const",		&attr_fun_op,		.mods = MOD_FCONST),
 	D("gnu_inline",		&attr_fun_op,		.mods = MOD_GNU_INLINE),
 	D("nodiscard",		&ignore_attr_op),
 	D("fallthrough",	&ignore_attr_op),
-	D("unsequenced",	&attr_fun_op,		.mods = MOD_PURE),
+	D("unsequenced",	&attr_fun_op,		.mods = MOD_FCONST),
 	D("reproducible",	&attr_fun_op,		.mods = MOD_PURE),
 
 	/* Modes */
@@ -985,6 +987,8 @@ static struct token *struct_union_enum_specifier(enum type type,
 	apply_ctype(token->pos, &sym->ctype, &attr.ctype);
 	sym->packed |= attr.packed;
 	sym->has_aligned |= attr.has_aligned;
+	if (attr.explicit_alignment > sym->explicit_alignment)
+		sym->explicit_alignment = attr.explicit_alignment;
 	if (sym->type == SYM_ENUM && sym->packed && sym->has_aligned)
 		warning(sym->pos,
 			"Clang and GCC treat 'packed' and 'aligned' attributes on enum types differently");
@@ -1462,8 +1466,10 @@ static struct token *ignore_attribute(struct token *token, struct symbol *attr,
 
 static struct token *attribute_packed(struct token *token, struct symbol *attr, struct decl_state *ctx)
 {
-	if (!ctx->ctype.alignment)
+	if (!ctx->explicit_alignment)
 		ctx->ctype.alignment = 1;
+	else
+		ctx->ctype.alignment = ctx->explicit_alignment;
 	ctx->packed = 1;
 	return token;
 }
@@ -1481,9 +1487,17 @@ static struct token *attribute_aligned(struct token *token, struct symbol *attr,
 	if (alignment <= 0 || alignment > INT_MAX || (alignment & (alignment - 1))) {
 		warning(token->pos, "I don't like non-power-of-2 alignments");
 		return token;
-	} else if (alignment > ctx->ctype.alignment)
-		ctx->ctype.alignment = alignment;
+	} else {
+		if (alignment > ctx->explicit_alignment)
+			ctx->explicit_alignment = alignment;
+		if (ctx->packed)
+			ctx->ctype.alignment = ctx->explicit_alignment;
+		else if (alignment > ctx->ctype.alignment)
+			ctx->ctype.alignment = alignment;
+	}
 	ctx->has_aligned = 1;
+	ctx->ctype.modifiers |= MOD_ALIGNED;
+	ctx->typedef_mods &= ~MOD_ALIGNED;
 	return token;
 }
 
@@ -1522,6 +1536,7 @@ static void apply_qualifier(struct position *pos, struct ctype *ctx, unsigned lo
 static struct token *attribute_modifier(struct token *token, struct symbol *attr, struct decl_state *ctx)
 {
 	apply_mod(&token->pos, &ctx->ctype.modifiers, attr->ctype.modifiers);
+	ctx->typedef_mods &= ~attr->ctype.modifiers;
 	return token;
 }
 
@@ -1779,8 +1794,9 @@ static struct token *attribute_specifier(struct token *token, struct symbol *sym
 
 static unsigned long decl_modifiers(struct decl_state *ctx)
 {
-	unsigned long mods = ctx->ctype.modifiers & MOD_DECLARE;
-	ctx->ctype.modifiers &= ~MOD_DECLARE;
+	unsigned long mask = MOD_DECLARE & ~ctx->typedef_mods;
+	unsigned long mods = ctx->ctype.modifiers & mask;
+	ctx->ctype.modifiers &= ~mask;
 	return ctx->storage_class | mods;
 }
 
@@ -1853,8 +1869,16 @@ static struct token *alignas_specifier(struct token *token, struct symbol *sym,
 		warning(token->pos, "non-power-of-2 alignment");
 		return token;
 	}
-	if (alignment > ctx->ctype.alignment)
-		ctx->ctype.alignment = alignment;
+	if (alignment > 0) {
+		if ((unsigned int)alignment > ctx->explicit_alignment)
+			ctx->explicit_alignment = alignment;
+		if (ctx->packed)
+			ctx->ctype.alignment = ctx->explicit_alignment;
+		else if ((unsigned int)alignment > ctx->ctype.alignment)
+			ctx->ctype.alignment = alignment;
+		ctx->ctype.modifiers |= MOD_ALIGNED;
+		ctx->typedef_mods &= ~MOD_ALIGNED;
+	}
 	return token;
 }
 
@@ -1972,9 +1996,12 @@ static struct token *declaration_specifiers(struct token *token, struct decl_sta
 		if (s->type != SYM_KEYWORD) {
 			if (seen & Set_Any)
 				break;
+			unsigned long prev_mods = ctx->ctype.modifiers;
 			seen |= Set_S | Set_T;
 			ctx->ctype.base_type = s->ctype.base_type;
 			apply_ctype(token->pos, &ctx->ctype, &s->ctype);
+			ctx->typedef_mods |= (s->ctype.modifiers & ~prev_mods) &
+					     (MOD_ALIGNED | MOD_WEAK | MOD_NONSTRING);
 			if (s->ctype.alignment > ctx->typedef_alignment)
 				ctx->typedef_alignment = s->ctype.alignment;
 			token = token->next;
@@ -2464,6 +2491,7 @@ static struct token *pointer(struct token *token, struct decl_state *ctx)
 		ctx->ctype.as = NULL;
 		ctx->ctype.contexts = NULL;
 		ctx->ctype.alignment = 0;
+		ctx->explicit_alignment = 0;
 		ctx->typedef_alignment = 0;
 
 		token = handle_qualifiers(token->next, ctx);
@@ -2528,12 +2556,28 @@ static struct token *handle_bitfield(struct token *token, struct decl_state *ctx
 	return token;
 }
 
+static void apply_fn_ptr_modifiers(struct symbol *decl, unsigned long f_modifiers)
+{
+	struct symbol *base;
+
+	if (!decl)
+		return;
+	f_modifiers |= decl->ctype.modifiers & MOD_FUN_ATTR;
+	if (!f_modifiers)
+		return;
+	base = decl->ctype.base_type;
+	if (base && base->type == SYM_PTR &&
+	    base->ctype.base_type && base->ctype.base_type->type == SYM_FN)
+		base->ctype.modifiers |= f_modifiers;
+}
+
 static struct token *declaration_list(struct token *token, struct symbol_list **list)
 {
 	struct decl_state ctx = {.prefer_abstract = 0};
 	struct ctype saved;
-	unsigned long mod;
-	unsigned int saved_td_align;
+	unsigned long mod, saved_f_modifiers;
+	unsigned int saved_explicit_alignment, saved_td_align;
+	unsigned char saved_packed;
 
 	token = declaration_specifiers(token, &ctx);
 	if (ctx.opaque_fixed_enum)
@@ -2541,12 +2585,18 @@ static struct token *declaration_list(struct token *token, struct symbol_list **
 			     "'enum' underlying type may not be specified here");
 	mod = decl_modifiers(&ctx);
 	saved = ctx.ctype;
+	saved_packed = ctx.packed;
+	saved_explicit_alignment = ctx.explicit_alignment;
 	saved_td_align = ctx.typedef_alignment;
+	saved_f_modifiers = ctx.f_modifiers;
 	for (;;) {
 		struct symbol *decl = alloc_symbol(token->pos, SYM_NODE);
 		ctx.cleanup = NULL;
 		ctx.ident_modifiers = 0;
+		ctx.packed = saved_packed;
+		ctx.explicit_alignment = saved_explicit_alignment;
 		ctx.typedef_alignment = saved_td_align;
+		ctx.f_modifiers = saved_f_modifiers;
 		ctx.ident = &decl->ident;
 
 		token = declarator(token, &ctx);
@@ -2560,6 +2610,9 @@ static struct token *declaration_list(struct token *token, struct symbol_list **
 		decl->ctype = ctx.ctype;
 		decl->ctype.modifiers |= mod;
 		decl->cleanup = ctx.cleanup;
+		decl->packed = ctx.packed;
+		decl->explicit_alignment = ctx.explicit_alignment;
+		apply_fn_ptr_modifiers(decl, ctx.f_modifiers);
 		decl->endpos = token->pos;
 		if (ctx.has_auto) {
 			sparse_error(
@@ -2623,6 +2676,9 @@ static struct token *parameter_declaration(struct token *token, struct symbol *s
 	apply_modifiers(token->pos, &ctx);
 	sym->ctype = ctx.ctype;
 	sym->ctype.modifiers |= decl_modifiers(&ctx);
+	sym->packed = ctx.packed;
+	sym->explicit_alignment = ctx.explicit_alignment;
+	apply_fn_ptr_modifiers(sym, ctx.f_modifiers);
 	sym->endpos = token->pos;
 	sym->forced_arg = ctx.forced;
 	if (ctx.has_auto || ctx.autotype) {
@@ -2691,7 +2747,9 @@ struct token *typename(struct token *token, struct symbol **p, int *forced)
 	ctx.ctype.modifiers |= ctx.ident_modifiers;
 	apply_modifiers(token->pos, &ctx);
 	sym->ctype = ctx.ctype;
+	sym->packed = ctx.packed;
 	sym->c23_auto = ctx.autotype && ctx.has_auto;
+	sym->explicit_alignment = ctx.explicit_alignment;
 	sym->endpos = token->pos;
 	if (ctx.storage_class == MOD_USERTYPE)
 		sym->explicit_typedef = 1;
@@ -2702,6 +2760,10 @@ struct token *typename(struct token *token, struct symbol **p, int *forced)
 		sym->ctype.modifiers |= MOD_CONSTEXPR;
 	}
 	sym->ctype.modifiers |= mod | decl_modifiers(&ctx);
+	if (sym->ctype.base_type && sym->ctype.base_type->type == SYM_FN)
+		sym->ctype.modifiers |= ctx.f_modifiers;
+	else
+		apply_fn_ptr_modifiers(sym, ctx.f_modifiers);
 	if (forced)
 		*forced = ctx.forced;
 	return token;
@@ -3783,10 +3845,10 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
 	struct symbol *decl;
 	struct decl_state ctx = { .ident = &ident };
 	struct ctype saved;
-	unsigned long saved_f_modifiers;
-	unsigned int saved_td_align;
 	struct symbol *base_type;
-	unsigned long mod;
+	unsigned long mod, saved_f_modifiers;
+	unsigned int saved_explicit_alignment, saved_td_align;
+	unsigned char saved_packed;
 	int is_typedef;
 	bool delay_bind = false;
 
@@ -3822,9 +3884,11 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
 		sparse_error(ctx.opaque_fixed_enum_pos,
 			     "'enum' underlying type may not be specified here");
 
-	saved = ctx.ctype;
-	saved_f_modifiers = ctx.f_modifiers;
+	saved_packed = ctx.packed;
+	saved_explicit_alignment = ctx.explicit_alignment;
 	saved_td_align = ctx.typedef_alignment;
+	saved_f_modifiers = ctx.f_modifiers;
+	saved = ctx.ctype;
 	token = declarator(token, &ctx);
 	token = handle_asm_name(token, &ctx);
 	token = handle_attributes(token, &ctx);
@@ -3834,7 +3898,10 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
 	decl->ctype = ctx.ctype;
 	decl->ctype.modifiers |= mod;
 	decl->cleanup = ctx.cleanup;
+	decl->packed = ctx.packed;
 	decl->c23_auto = ctx.autotype && ctx.has_auto;
+	decl->explicit_alignment = ctx.explicit_alignment;
+	apply_fn_ptr_modifiers(decl, ctx.f_modifiers);
 	decl->endpos = token->pos;
 
 	/* Just a type declaration? */
@@ -4089,11 +4156,13 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
 		token = token->next;
 		ident = NULL;
 		decl = alloc_symbol(token->pos, SYM_NODE);
+		ctx.packed = saved_packed;
+		ctx.explicit_alignment = saved_explicit_alignment;
+		ctx.typedef_alignment = saved_td_align;
+		ctx.f_modifiers = saved_f_modifiers;
 		ctx.ctype = saved;
 		ctx.cleanup = NULL;
 		ctx.ident_modifiers = 0;
-		ctx.f_modifiers = saved_f_modifiers;
-		ctx.typedef_alignment = saved_td_align;
 		token = handle_attributes(token, &ctx);
 		token = declarator(token, &ctx);
 		token = handle_asm_name(token, &ctx);
@@ -4103,7 +4172,10 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
 		decl->ctype = ctx.ctype;
 		decl->ctype.modifiers |= mod;
 		decl->cleanup = ctx.cleanup;
+		decl->packed = ctx.packed;
 		decl->c23_auto = ctx.autotype && ctx.has_auto;
+		decl->explicit_alignment = ctx.explicit_alignment;
+		apply_fn_ptr_modifiers(decl, ctx.f_modifiers);
 		decl->endpos = token->pos;
 		if (!ident) {
 			sparse_error(token->pos, "expected identifier name in type definition");
diff --git a/show-parse.c b/show-parse.c
index 9ed95f141cfe..f44a5f052ae7 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -138,6 +138,7 @@ static const char *show_modifiers(unsigned long mod, int term)
 		{ MOD_NODEREF, "[noderef]" },
 		{ MOD_NORETURN, "[noreturn]" },
 		{ MOD_PURE, "[pure]" },
+		{ MOD_FCONST, "[const]" },
 		{ MOD_SAFE, "[safe]" },
 		{ MOD_SIGNED, "[signed]" },
 		{ MOD_TLS, "[tls]" },
@@ -145,6 +146,9 @@ static const char *show_modifiers(unsigned long mod, int term)
 		{ MOD_UNSIGNED, "[unsigned]" },
 		{ MOD_UNUSED, "[unused]" },
 		{ MOD_USERTYPE, "[usertype]" },
+		{ MOD_WEAK, "[weak]" },
+		{ MOD_ALIGNED, "[aligned]" },
+		{ MOD_NONSTRING, "[nonstring]" },
 	};
 
 	for (i = 0; i < ARRAY_SIZE(mod_names); i++) {
@@ -1188,6 +1192,7 @@ int show_expression(struct expression *expr)
 	case EXPR_PTRSIZEOF:
 	case EXPR_ALIGNOF:
 	case EXPR_OFFSETOF:
+	case EXPR_HAS_ATTRIBUTE:
 		warning(expr->pos, "invalid expression after evaluation");
 		return 0;
 	case EXPR_CAST:
diff --git a/simplify.c b/simplify.c
index 88f261da297e..b93fbf950d3d 100644
--- a/simplify.c
+++ b/simplify.c
@@ -365,7 +365,7 @@ int kill_insn(struct instruction *insn, int force)
 			/* a "pure" function can be killed too */
 			struct symbol *fntype = first_symbol(insn->fntypes);
 
-			if (!(fntype->ctype.modifiers & MOD_PURE))
+			if (!(fntype->ctype.modifiers & (MOD_PURE | MOD_FCONST)))
 				return 0;
 		}
 		kill_use_list(insn->arguments);
diff --git a/symbol.c b/symbol.c
index 69fb0670c341..d2f308519cdc 100644
--- a/symbol.c
+++ b/symbol.c
@@ -141,19 +141,23 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
 		info->flex_array = sym;
 	}
 
-	alignment = info->packed ? 1 : sym->ctype.alignment;
+	bool packed = info->packed || sym->packed;
+
+	alignment = packed ? (sym->explicit_alignment ? : 1) : sym->ctype.alignment;
 	align_bit_mask = bytes_to_bits(alignment) - 1;
 
 	/*
 	 * Bitfields have some very special rules..
 	 */
 	if (is_bitfield_type (sym)) {
+		if (sym->explicit_alignment)
+			bit_size = (bit_size + align_bit_mask) & ~align_bit_mask;
 		unsigned long bit_offset = bit_size & align_bit_mask;
 		int room = bitfield_base_size(sym) - bit_offset;
 		// Zero-width fields just fill up the unit.
 		int width = base_size ? : (bit_offset ? room : 0);
 
-		if (width > room && !info->packed) {
+		if (width > room && !packed) {
 			bit_size = (bit_size + align_bit_mask) & ~align_bit_mask;
 			bit_offset = 0;
 		}
@@ -163,7 +167,7 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
 		info->bit_size = bit_size + width;
 		// warning (sym->pos, "bitfield: offset=%d:%d  size=:%d", sym->offset, sym->bit_offset, width);
 
-		if (info->packed && sym->type == SYM_NODE)
+		if (packed && sym->type == SYM_NODE)
 			sym->packed = 1;
 		return;
 	}
@@ -232,10 +236,13 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance
 		if (!member->ident)
 			examine_anonymous_member(member);
 
-		if (member->ctype.alignment > info.max_align && !sym->packed) {
+		unsigned long member_align = (sym->packed || member->packed)
+			? (member->explicit_alignment ? : 1)
+			: member->ctype.alignment;
+		if (member_align > info.max_align) {
 			// Unnamed bitfields do not affect alignment.
 			if (member->ident || !is_bitfield_type(member))
-				info.max_align = member->ctype.alignment;
+				info.max_align = member_align;
 		}
 
 		if (has_flexible_array(member))
@@ -245,7 +252,7 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance
 		fn(member, &info);
 	} END_FOR_EACH_PTR(member);
 
-	if (!sym->ctype.alignment)
+	if (sym->ctype.alignment < info.max_align)
 		sym->ctype.alignment = info.max_align;
 	bit_size = info.bit_size;
 	if (info.align_size) {
@@ -566,7 +573,7 @@ static struct symbol * examine_node_type(struct symbol *sym)
 	/* Pick up signedness information into the node */
 	sym->ctype.modifiers |= (MOD_SIGNEDNESS & base_type->ctype.modifiers);
 
-	if (!sym->ctype.alignment)
+	if (!sym->ctype.alignment || (!sym->packed && sym->ctype.alignment < alignment))
 		sym->ctype.alignment = alignment;
 
 	/* Unsized array? The size might come from the initializer.. */
@@ -885,8 +892,12 @@ static void inherit_declaration(struct symbol *sym, struct symbol *prev)
 {
 	unsigned long mods = prev->ctype.modifiers;
 
-	// inherit function attributes
-	sym->ctype.modifiers |= mods & MOD_FUN_ATTR;
+	// inherit function and declaration attributes
+	sym->ctype.modifiers |= mods & (MOD_FUN_ATTR | MOD_WEAK | MOD_ALIGNED | MOD_NONSTRING);
+	if ((mods & MOD_ALIGNED) && prev->ctype.alignment > sym->ctype.alignment)
+		sym->ctype.alignment = prev->ctype.alignment;
+	if (prev->explicit_alignment > sym->explicit_alignment)
+		sym->explicit_alignment = prev->explicit_alignment;
 }
 
 void check_declaration(struct symbol *sym)
@@ -906,6 +917,7 @@ void check_declaration(struct symbol *sym)
 		if (sym->ctype.modifiers & MOD_EXTERN) {
 			if ((next->ctype.modifiers & (MOD_TOPLEVEL|MOD_STATIC)) == MOD_TOPLEVEL) {
 				sym->same_symbol = next;
+				inherit_declaration(sym, next);
 				return;
 			}
 		}
diff --git a/symbol.h b/symbol.h
index 691213a0b454..cd9268703192 100644
--- a/symbol.h
+++ b/symbol.h
@@ -117,6 +117,8 @@ struct decl_state {
 	unsigned long f_modifiers;		// function attributes
 	unsigned long ident_modifiers;		// declarator-id C23 attributes
 	unsigned long storage_class;
+	unsigned long typedef_mods;
+	unsigned int explicit_alignment;
 	unsigned int typedef_alignment;
 	unsigned char prefer_abstract;
 	unsigned char autotype;
@@ -199,6 +201,7 @@ struct symbol {
 			unsigned long	offset;
 			int		bit_size;
 			int bitint_width;
+			unsigned int explicit_alignment;
 			unsigned int bit_offset : 8;
 			unsigned int bogus_linear : 1;
 			unsigned int variadic : 1;
@@ -276,8 +279,8 @@ struct symbol {
 #define MOD_USERTYPE		0x00020000
 #define MOD_CONSTEXPR 0x00040000
 #define MOD_BITINT 0x00080000
-// MOD UNUSED		0x00100000
-// MOD UNUSED		0x00200000
+#define MOD_WEAK		0x00100000
+#define MOD_ALIGNED		0x00200000
 
 #define MOD_UNUSED		0x00400000
 #define MOD_SAFE		0x00800000	// non-null/non-trapping pointer
@@ -287,6 +290,8 @@ struct symbol {
 #define MOD_NODEREF		0x08000000
 #define MOD_NORETURN		0x10000000
 #define MOD_EXT_VISIBLE		0x20000000
+#define MOD_NONSTRING		0x40000000
+#define MOD_FCONST		0x80000000UL
 
 
 #define MOD_ACCESS	(MOD_ASSIGNED | MOD_ADDRESSABLE)
@@ -295,21 +300,22 @@ 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 | MOD_BITINT)
-#define MOD_IGNORE	(MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE | MOD_UNUSED | MOD_GNU_INLINE | MOD_CONSTEXPR)
+#define MOD_IGNORE	(MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE | MOD_UNUSED | MOD_GNU_INLINE | MOD_WEAK | MOD_ALIGNED | MOD_NONSTRING | MOD_CONSTEXPR)
 #define MOD_QUALIFIER	(MOD_CONST | MOD_VOLATILE | MOD_RESTRICT)
 /* modifiers for function attributes */
-#define MOD_FUN_ATTR	(MOD_PURE|MOD_NORETURN)
+#define MOD_FUN_ATTR	(MOD_PURE|MOD_NORETURN|MOD_FCONST)
 #define MOD_PTRINHERIT	(MOD_QUALIFIER | MOD_ATOMIC | MOD_NODEREF | MOD_FUN_ATTR | MOD_NOCAST)
 /* modifiers preserved by typeof() operator */
 #define MOD_TYPEOF	(MOD_QUALIFIER | MOD_ATOMIC | MOD_NOCAST | MOD_SPECIFIER)
 /* like cvr-qualifiers but 'reversed' (OK: source <= target) */
-#define MOD_REV_QUAL	(MOD_PURE|MOD_NORETURN)
+#define MOD_REV_QUAL	(MOD_PURE|MOD_NORETURN|MOD_FCONST)
 /* do not warn when these are duplicated */
-#define MOD_DUP_OK	(MOD_UNUSED|MOD_GNU_INLINE|MOD_USERTYPE)
+#define MOD_DUP_OK	(MOD_UNUSED|MOD_GNU_INLINE|MOD_WEAK|MOD_ALIGNED|MOD_USERTYPE|MOD_NONSTRING|MOD_FUN_ATTR)
 /* 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 | MOD_CONSTEXPR)
+	 MOD_PURE | MOD_NORETURN | MOD_FCONST | MOD_EXT_VISIBLE |           \
+	 MOD_CONSTEXPR | MOD_WEAK | MOD_ALIGNED | MOD_NONSTRING)
 
 /* Current parsing/evaluation function */
 extern struct symbol *current_fn;
diff --git a/validation/builtin-has-attribute-err.c b/validation/builtin-has-attribute-err.c
new file mode 100644
index 000000000000..cb756fdd2bab
--- /dev/null
+++ b/validation/builtin-has-attribute-err.c
@@ -0,0 +1,19 @@
+static int x;
+
+static int test_err(void)
+{
+	return __builtin_has_attribute(x, 123) +
+	       __builtin_has_attribute(x, "aligned") +
+	       __builtin_has_attribute(x, );
+}
+
+/*
+ * check-name: builtin-has-attribute-err
+ * check-command: sparse $file
+ *
+ * check-error-start
+builtin-has-attribute-err.c:5:43: error: expected attribute name
+builtin-has-attribute-err.c:6:43: error: expected attribute name
+builtin-has-attribute-err.c:7:43: error: expected attribute name
+ * check-error-end
+ */
diff --git a/validation/builtin-has-attribute-more.c b/validation/builtin-has-attribute-more.c
new file mode 100644
index 000000000000..295301d06930
--- /dev/null
+++ b/validation/builtin-has-attribute-more.c
@@ -0,0 +1,157 @@
+extern void fn_noreturn(void) __attribute__((noreturn));
+_Static_assert(__builtin_has_attribute(fn_noreturn, noreturn), "noreturn");
+_Static_assert(__builtin_has_attribute(fn_noreturn, __noreturn__), "__noreturn__");
+
+extern int fn_pure(int) __attribute__((pure));
+_Static_assert(__builtin_has_attribute(fn_pure, pure), "pure");
+_Static_assert(__builtin_has_attribute(fn_pure, __pure__), "__pure__");
+_Static_assert(!__builtin_has_attribute(fn_pure, const), "pure is not const");
+_Static_assert(!__builtin_has_attribute(fn_pure, __const__), "pure is not __const__");
+
+extern int fn_const(int) __attribute__((const));
+_Static_assert(__builtin_has_attribute(fn_const, const), "const");
+_Static_assert(__builtin_has_attribute(fn_const, __const__), "__const__");
+_Static_assert(!__builtin_has_attribute(fn_const, pure), "const is not pure");
+_Static_assert(!__builtin_has_attribute(fn_const, __pure__), "const is not __pure__");
+
+/* const on object/member declarations vs expressions/types */
+static const int ci = 5;
+static int vi = 5;
+static const int ca[3] = { 0 };
+static const int *cip;
+static int * const cpi = (void *)0;
+
+_Static_assert(__builtin_has_attribute(ci, const), "const var");
+_Static_assert(!__builtin_has_attribute(vi, const), "non-const var");
+_Static_assert(__builtin_has_attribute(ca, const), "const array");
+_Static_assert(!__builtin_has_attribute(cip, const), "ptr to const");
+_Static_assert(!__builtin_has_attribute(*cip, const), "deref ptr to const");
+_Static_assert(__builtin_has_attribute(cpi, const), "const ptr");
+_Static_assert(!__builtin_has_attribute(const int, const), "const int typename");
+
+struct SConst {
+	const int cfield;
+	int plain;
+};
+static struct SConst sc;
+static const struct SConst csc = { 0, 0 };
+
+_Static_assert(__builtin_has_attribute(sc.cfield, const), "const field");
+_Static_assert(__builtin_has_attribute(((struct SConst *)0)->cfield, const), "arrow const field");
+_Static_assert(!__builtin_has_attribute(sc.plain, const), "plain field");
+_Static_assert(!__builtin_has_attribute(csc.plain, const), "const struct plain field");
+_Static_assert(!__builtin_has_attribute(((const struct SConst *)0)->plain, const), "const arrow plain field");
+
+/* aligned, _Alignas, typedef pointer non-leakage, and member non-inheritance */
+typedef int aligned_int __attribute__((aligned(16)));
+static aligned_int ax;
+static aligned_int *ap;
+static aligned_int mx, *mp;
+static _Alignas(16) int a16;
+static _Alignas(0) int a0;
+
+_Static_assert(_Alignof(ax) == 16, "ax align 16");
+_Static_assert(_Alignof(ap) == sizeof(void *), "ap ptr align");
+_Static_assert(_Alignof(mx) == 16, "mx align 16");
+_Static_assert(_Alignof(mp) == sizeof(void *), "mp ptr align");
+_Static_assert(__builtin_has_attribute(aligned_int, aligned), "typedef aligned");
+_Static_assert(__builtin_has_attribute(ax, aligned), "ax aligned");
+_Static_assert(!__builtin_has_attribute(ap, aligned), "ap not aligned");
+_Static_assert(__builtin_has_attribute(*ap, aligned), "*ap aligned");
+_Static_assert(__builtin_has_attribute(mx, aligned), "mx aligned");
+_Static_assert(!__builtin_has_attribute(mp, aligned), "mp not aligned");
+_Static_assert(__builtin_has_attribute(*mp, aligned), "*mp aligned");
+_Static_assert(__builtin_has_attribute(a16, aligned), "_Alignas(16) aligned");
+_Static_assert(!__builtin_has_attribute(a0, aligned), "_Alignas(0) not aligned");
+
+struct SAligned {
+	int a;
+	int b __attribute__((aligned(8)));
+} __attribute__((aligned(16)));
+static struct SAligned sa;
+static struct SAligned sa_var __attribute__((aligned(32)));
+
+_Static_assert(__builtin_has_attribute(struct SAligned, aligned), "struct SAligned aligned");
+_Static_assert(!__builtin_has_attribute(sa.a, aligned), "sa.a not aligned");
+_Static_assert(!__builtin_has_attribute(sa_var.a, aligned), "sa_var.a not aligned");
+_Static_assert(__builtin_has_attribute(sa.b, aligned), "sa.b aligned");
+
+struct SOuter {
+	struct SAligned inner;
+	struct SAligned arr[2];
+};
+static struct SOuter *so_ptr;
+static struct SAligned sa_arr[2];
+
+_Static_assert(__builtin_has_attribute(so_ptr->inner.b, aligned), "nested member aligned");
+_Static_assert(!__builtin_has_attribute(so_ptr->inner.a, aligned), "nested member not aligned");
+_Static_assert(__builtin_has_attribute(sa_arr[1].b, aligned), "array subscript member aligned");
+_Static_assert(!__builtin_has_attribute(sa_arr[1].a, aligned), "array subscript member not aligned");
+_Static_assert(__builtin_has_attribute(so_ptr->arr[1].b, aligned), "nested array member aligned");
+
+/* typename operands */
+_Static_assert(__builtin_has_attribute(int __attribute__((aligned(8))), aligned), "typename aligned");
+_Static_assert(!__builtin_has_attribute(int, aligned), "typename int not aligned");
+_Static_assert(__builtin_has_attribute(struct __attribute__((packed)) { int x; }, packed), "typename pre-packed");
+_Static_assert(__builtin_has_attribute(struct { int x; } __attribute__((packed)), packed), "typename post-packed");
+_Static_assert(!__builtin_has_attribute(struct { int x; }, packed), "typename unpacked");
+extern void (*fp_noreturn)(void) __attribute__((noreturn));
+_Static_assert(__builtin_has_attribute(fp_noreturn, noreturn), "fnptr decl noreturn");
+extern int (*fp_after_ident [[unsequenced]])(int);
+extern int (*fn_ret_fp_a(void) [[unsequenced]])(int);
+extern int (*fn_ret_fp_b(void))(int) [[unsequenced]];
+extern int (*fn_ret_fp_c [[unsequenced]] (void))(int);
+_Static_assert(__builtin_has_attribute(fp_after_ident, unsequenced), "fnptr after ident");
+_Static_assert(__builtin_has_attribute(fn_ret_fp_a, unsequenced), "nested fn unseq");
+_Static_assert(!__builtin_has_attribute(fn_ret_fp_a(), unsequenced), "ret fp not unseq");
+_Static_assert(!__builtin_has_attribute(fn_ret_fp_b, unsequenced), "outer fn not unseq");
+_Static_assert(__builtin_has_attribute(fn_ret_fp_b(), unsequenced), "ret fp unseq");
+_Static_assert(__builtin_has_attribute(fn_ret_fp_c, unsequenced), "fn after ident unseq");
+_Static_assert(!__builtin_has_attribute(fn_ret_fp_c(), unsequenced), "fn after ident ret fp");
+
+/* Redeclaration attribute inheritance */
+void ext_redecl_weak(void) __attribute__((weak));
+void ext_redecl_weak(void) {}
+_Static_assert(__builtin_has_attribute(ext_redecl_weak, weak), "redecl weak");
+
+int ext_redecl_const(int x) __attribute__((const));
+int ext_redecl_const(int x) { return x; }
+_Static_assert(__builtin_has_attribute(ext_redecl_const, const), "redecl const");
+_Static_assert(!__builtin_has_attribute(ext_redecl_const, pure), "redecl const not pure");
+
+int ext_redecl_pure(int x) __attribute__((pure));
+int ext_redecl_pure(int x) { return x; }
+_Static_assert(__builtin_has_attribute(ext_redecl_pure, pure), "redecl pure");
+_Static_assert(!__builtin_has_attribute(ext_redecl_pure, const), "redecl pure not const");
+
+extern int ext_redecl_aligned __attribute__((aligned(16)));
+int ext_redecl_aligned;
+_Static_assert(__builtin_has_attribute(ext_redecl_aligned, aligned), "redecl aligned");
+_Static_assert(_Alignof(ext_redecl_aligned) == 16, "redecl align 16");
+
+#ifdef __CHECKER__
+static int __attribute__((noderef)) *ptr_noderef;
+_Static_assert(__builtin_has_attribute(*ptr_noderef, noderef), "noderef");
+_Static_assert(__builtin_has_attribute(*ptr_noderef, __noderef__), "__noderef__");
+_Static_assert(!__builtin_has_attribute(ptr_noderef, noderef), "ptr itself not noderef");
+
+static int __attribute__((address_space(1))) *ptr_as;
+_Static_assert(__builtin_has_attribute(*ptr_as, address_space), "address_space");
+_Static_assert(__builtin_has_attribute(*ptr_as, __address_space__), "__address_space__");
+_Static_assert(!__builtin_has_attribute(ptr_as, address_space), "ptr itself not address_space");
+
+typedef unsigned int __attribute__((bitwise)) __be32;
+static __be32 be_var;
+static __be32 *be_ptr;
+_Static_assert(__builtin_has_attribute(__be32, bitwise), "__be32 bitwise");
+_Static_assert(__builtin_has_attribute(__be32, __bitwise__), "__be32 __bitwise__");
+_Static_assert(__builtin_has_attribute(be_var, bitwise), "be_var bitwise");
+_Static_assert(__builtin_has_attribute(*be_ptr, bitwise), "*be_ptr bitwise");
+_Static_assert(!__builtin_has_attribute(be_ptr, bitwise), "be_ptr not bitwise");
+_Static_assert(!__builtin_has_attribute(int, bitwise), "int not bitwise");
+#endif
+
+/*
+ * check-name: builtin-has-attribute-more
+ * check-command: sparse -Wno-decl $file
+ */
diff --git a/validation/builtin-has-attribute-nonstring.c b/validation/builtin-has-attribute-nonstring.c
new file mode 100644
index 000000000000..7662cbc13040
--- /dev/null
+++ b/validation/builtin-has-attribute-nonstring.c
@@ -0,0 +1,35 @@
+struct s_msg {
+	char str[16];
+	char nonstr[16] __attribute__((__nonstring__));
+};
+
+static struct s_msg msg;
+static char global_nonstr[32] __attribute__((nonstring));
+static char global_str[32];
+static char *ptr_nonstr __attribute__((nonstring));
+static char *ptr_str;
+
+extern char ext_nonstr[8] __attribute__((nonstring));
+char ext_nonstr[8];
+
+_Static_assert(__has_attribute(nonstring), "has_attribute nonstring");
+_Static_assert(__has_attribute(__nonstring__), "has_attribute __nonstring__");
+_Static_assert(__builtin_has_attribute(msg.nonstr, nonstring), "msg.nonstr has nonstring");
+_Static_assert(__builtin_has_attribute(msg.nonstr, __nonstring__), "msg.nonstr has __nonstring__");
+_Static_assert(!__builtin_has_attribute(msg.str, nonstring), "msg.str does not have nonstring");
+_Static_assert(__builtin_has_attribute(global_nonstr, nonstring), "global_nonstr has nonstring");
+_Static_assert(!__builtin_has_attribute(global_str, nonstring), "global_str does not have nonstring");
+_Static_assert(__builtin_has_attribute(ptr_nonstr, nonstring), "ptr_nonstr has nonstring");
+_Static_assert(!__builtin_has_attribute(ptr_str, nonstring), "ptr_str does not have nonstring");
+_Static_assert(__builtin_has_attribute(ext_nonstr, nonstring), "redecl nonstring");
+
+#ifdef __CHECKER__
+typedef char nonstr_arr[8] __attribute__((nonstring));
+static nonstr_arr *ns_p;
+_Static_assert(!__builtin_has_attribute(ns_p, nonstring), "ptr to typedef nonstring does not leak");
+#endif
+
+/*
+ * check-name: builtin-has-attribute-nonstring
+ * check-command: sparse -Wno-decl $file
+ */
diff --git a/validation/builtins-kernel.c b/validation/builtins-kernel.c
index 05c13f4c1eae..f4b1d4b45b68 100644
--- a/validation/builtins-kernel.c
+++ b/validation/builtins-kernel.c
@@ -2,6 +2,10 @@
 #error "missing __builtin_dynamic_object_size"
 #endif
 
+#if !__has_builtin(__builtin_has_attribute)
+#error "missing __builtin_has_attribute"
+#endif
+
 #if !__has_builtin(__builtin_thread_pointer)
 #error "missing __builtin_thread_pointer"
 #endif
@@ -14,6 +18,33 @@
 #error "missing __builtin_counted_by_ref"
 #endif
 
+extern int weak_var __attribute__((__weak__));
+_Static_assert(__builtin_has_attribute(weak_var, __weak__), "weak_var has __weak__");
+_Static_assert(__builtin_has_attribute(weak_var, weak), "weak_var has weak");
+_Static_assert(__builtin_has_attribute(*&weak_var, __weak__), "*&weak_var has __weak__");
+
+struct s_packed {
+	int a;
+	char b;
+} __attribute__((__packed__));
+
+_Static_assert(__builtin_has_attribute(struct s_packed, packed), "s_packed has packed");
+_Static_assert(__builtin_has_attribute(struct s_packed, __packed__), "s_packed has __packed__");
+
+struct s_normal {
+	int a;
+	char b;
+	int c __attribute__((__packed__));
+};
+static struct s_normal s_norm_inst;
+
+_Static_assert(!__builtin_has_attribute(struct s_normal, packed), "s_normal not packed");
+_Static_assert(__builtin_has_attribute(s_norm_inst.c, packed), "s_norm_inst.c packed");
+_Static_assert(!__builtin_has_attribute(weak_var, packed), "weak_var not packed");
+
+static int aligned_var __attribute__((aligned(16)));
+_Static_assert(__builtin_has_attribute(aligned_var, aligned), "aligned_var has aligned");
+
 struct fam {
 	int count;
 	char arr[];
diff --git a/validation/struct-packed-aligned-layout.c b/validation/struct-packed-aligned-layout.c
new file mode 100644
index 000000000000..c85a4a24ac7f
--- /dev/null
+++ b/validation/struct-packed-aligned-layout.c
@@ -0,0 +1,135 @@
+typedef int aligned16 __attribute__((aligned(16)));
+
+/* 1. Post-brace packed struct with explicitly aligned member */
+struct S1 {
+	char a;
+	int b __attribute__((aligned(8)));
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct S1, b) == 8, "S1 offset b");
+_Static_assert(_Alignof(struct S1) == 8, "S1 align");
+_Static_assert(sizeof(struct S1) == 16, "S1 size");
+
+/* 2. Pre-tag packed struct with explicitly aligned member */
+struct __attribute__((packed)) S2 {
+	char a;
+	int b __attribute__((aligned(8)));
+};
+_Static_assert(__builtin_offsetof(struct S2, b) == 8, "S2 offset b");
+_Static_assert(_Alignof(struct S2) == 8, "S2 align");
+_Static_assert(sizeof(struct S2) == 16, "S2 size");
+
+/* 3. Struct with both packed and aligned(8) on the struct itself */
+struct S3 {
+	char a;
+	int b;
+} __attribute__((packed, aligned(8)));
+_Static_assert(__builtin_offsetof(struct S3, b) == 1, "S3 offset b");
+_Static_assert(_Alignof(struct S3) == 8, "S3 align");
+_Static_assert(sizeof(struct S3) == 8, "S3 size");
+
+/* 4. Nested S1 inside packed S4 without explicit aligned on inner */
+struct S4 {
+	char a;
+	struct S1 inner;
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct S4, inner) == 1, "S4 offset inner");
+_Static_assert(_Alignof(struct S4) == 1, "S4 align");
+_Static_assert(sizeof(struct S4) == 17, "S4 size");
+
+/* 5. Nested S3 inside packed S5 without explicit aligned on inner */
+struct S5 {
+	char a;
+	struct S3 inner;
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct S5, inner) == 1, "S5 offset inner");
+_Static_assert(_Alignof(struct S5) == 1, "S5 align");
+_Static_assert(sizeof(struct S5) == 9, "S5 size");
+
+/* 6. Nested S1 inside packed S6 with smaller explicit aligned(4) on inner */
+struct S6 {
+	char a;
+	struct S1 inner __attribute__((aligned(4)));
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct S6, inner) == 4, "S6 offset inner");
+_Static_assert(_Alignof(struct S6) == 4, "S6 align");
+_Static_assert(sizeof(struct S6) == 20, "S6 size");
+
+/* 7. Unpacked struct with member carrying both packed and aligned(8) */
+struct S7 {
+	char a;
+	int b __attribute__((packed, aligned(8)));
+};
+_Static_assert(__builtin_offsetof(struct S7, b) == 8, "S7 offset b");
+_Static_assert(_Alignof(struct S7) == 8, "S7 align");
+_Static_assert(sizeof(struct S7) == 16, "S7 size");
+
+/* 8. _Alignas(8) and _Alignas(0) inside packed structs */
+struct S8 {
+	char a;
+	_Alignas(8) int b;
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct S8, b) == 8, "S8 offset b");
+_Static_assert(_Alignof(struct S8) == 8, "S8 align");
+_Static_assert(sizeof(struct S8) == 16, "S8 size");
+
+struct S9 {
+	char a;
+	_Alignas(0) int b;
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct S9, b) == 1, "S9 offset b");
+_Static_assert(_Alignof(struct S9) == 1, "S9 align");
+_Static_assert(sizeof(struct S9) == 5, "S9 size");
+
+/* 9. Aligned typedef inside packed struct / packed field / pointer */
+struct ST1 {
+	char a;
+	aligned16 b;
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct ST1, b) == 1, "ST1 offset b");
+_Static_assert(_Alignof(struct ST1) == 1, "ST1 align");
+_Static_assert(sizeof(struct ST1) == 5, "ST1 size");
+
+struct ST2 {
+	char a;
+	aligned16 b __attribute__((packed));
+};
+_Static_assert(__builtin_offsetof(struct ST2, b) == 1, "ST2 offset b");
+_Static_assert(_Alignof(struct ST2) == 1, "ST2 align");
+_Static_assert(sizeof(struct ST2) == 5, "ST2 size");
+
+struct ST3 {
+	char a;
+	aligned16 b __attribute__((aligned(8)));
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct ST3, b) == 8, "ST3 offset b");
+_Static_assert(_Alignof(struct ST3) == 8, "ST3 align");
+_Static_assert(sizeof(struct ST3) == 16, "ST3 size");
+
+struct ST4 {
+	char a;
+	aligned16 *p;
+} __attribute__((packed));
+_Static_assert(__builtin_offsetof(struct ST4, p) == 1, "ST4 offset p");
+_Static_assert(_Alignof(struct ST4) == 1, "ST4 align");
+_Static_assert(sizeof(struct ST4) == 1 + sizeof(void *), "ST4 size");
+
+/* 10. Packed union with explicitly aligned member */
+union U1 {
+	char a;
+	int b __attribute__((aligned(8)));
+} __attribute__((packed));
+_Static_assert(_Alignof(union U1) == 8, "U1 align");
+_Static_assert(sizeof(union U1) == 8, "U1 size");
+
+/* 11. Packed struct with explicitly aligned bitfield */
+struct BF1 {
+	unsigned char a : 1;
+	unsigned int b : 3 __attribute__((aligned(8)));
+} __attribute__((packed));
+_Static_assert(_Alignof(struct BF1) == 8, "BF1 align");
+_Static_assert(sizeof(struct BF1) == 16, "BF1 size");
+
+/*
+ * check-name: struct-packed-aligned-layout
+ * check-command: sparse $file
+ */
diff --git a/validation/weak-const-no-fold.c b/validation/weak-const-no-fold.c
new file mode 100644
index 000000000000..5486a759226b
--- /dev/null
+++ b/validation/weak-const-no-fold.c
@@ -0,0 +1,15 @@
+const int __attribute__((weak)) weak_const = 10;
+
+int read_weak_const(void)
+{
+	return weak_const;
+}
+
+/*
+ * check-name: weak-const-no-fold
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: load\\.32.*\\[weak_const\\]
+ * check-output-excludes: ret\\.32.*\\$10
+ */

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