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
+ */
next prev 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