root/usr.bin/xlint/lint1/lint1.h
/* $NetBSD: lint1.h,v 1.241 2026/06/16 05:27:57 rillig Exp $ */

/*
 * Copyright (c) 1996 Christopher G. Demetriou.  All Rights Reserved.
 * Copyright (c) 1994, 1995 Jochen Pohl
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Jochen Pohl for
 *      The NetBSD Project.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "lint.h"
#include "op.h"

/*
 * A memory pool collects allocated objects that must be available until:
 * - the end of a block,
 * - the end of an expression, or
 * - the end of the translation unit.
 */
typedef struct memory_pool {
        struct memory_pool_item {
                void *p;
#ifdef DEBUG_MEM
                size_t size;
                const char *descr;
#endif
        } *items;
        size_t  len;
        size_t  cap;
} memory_pool;

/*
 * By default, all expressions are fully evaluated. In some cases such as the
 * sizeof operator, only the type of an expression is needed, but not its
 * value. In yet other cases such as a _Generic expression, not even the type
 * is needed, so just parsing the expression suffices.
 */
typedef enum {
        /* Only parse expressions, don't determine their type. */
        EM_PARSE,
        /* Determine the type of the expressions, but don't evaluate them. */
        EM_TYPE,
        /* Fully evaluate expressions. */
        EM_EVAL,
} evaluation_mode;

/* See saved_lwarn in cgram.y. */
#define LWARN_ALL       (-2)
#define LWARN_NONE      (-1)

/*
 * Describes the position of a declaration or anything else.
 *
 * FIXME: Just a single file:lineno pair is not enough to accurately describe
 *  the position of a symbol.  The whole inclusion path at that point must be
 *  stored as well.  This makes a difference for symbols from included
 *  headers, see print_stack_trace.
 */
typedef struct {
        const char *p_file;
        int     p_line;
        int     p_uniq;                 /* uniquifier */
} pos_t;

typedef struct {
        bool tq_const;
        bool tq_restrict;
        bool tq_volatile;
        bool tq_atomic;
} type_qualifiers;

typedef struct {
        bool used;
        bool noreturn;
        unsigned bit_width;
} type_attributes;

/* A bool, integer or floating-point value. */
typedef struct {
        tspec_t v_tspec;
        /*
         * Set if an integer constant is unsigned only in C90 and later, but
         * not in traditional C.
         *
         * See the operators table in oper.c, columns "l r".
         */
        bool    v_unsigned_since_c90;
        bool    v_char_constant;
        union {
                int64_t         integer;
                long double     floating;
        } u;
} val_t;

typedef struct sym sym_t;

/*
 * Structures of type struct_or_union uniquely identify structures. This can't
 * be done in structures of type type_t, because these are copied if they must
 * be modified. So it would not be possible to check if two structures are
 * identical by comparing the pointers to the type structures.
 *
 * If the structure has no tag name, its first typedef name is used to identify
 * the structure in lint2.
 */
typedef struct {
        unsigned int sou_size_in_bits;
        unsigned int sou_align;
        bool    sou_incomplete:1;
        sym_t   *sou_first_member;
        sym_t   *sou_tag;
        sym_t   *sou_first_typedef;
} struct_or_union;

/* The same as in struct_or_union, only for enums. */
typedef struct {
        bool    en_incomplete:1;
        sym_t   *en_first_enumerator;
        sym_t   *en_tag;
        sym_t   *en_first_typedef;
} enumeration;

/* The type of an expression, object or function. */
struct lint1_type {
        tspec_t t_tspec;        /* type specifier */
        bool    t_incomplete_array:1;
        bool    t_const:1;
        bool    t_volatile:1;
        bool    t_proto:1;      /* function prototype (u.params valid) */
        bool    t_identifier_params:1;  /* if FUNC and not t_proto */
        bool    t_vararg:1;     /* prototype with '...' */
        bool    t_noreturn:1;   /* function never returns normally */
        bool    t_typedef:1;    /* type defined with typedef */
        bool    t_typeof:1;     /* type defined with GCC's __typeof__ */
        bool    t_bitfield:1;
        /*
         * Either the type is currently an enum (having t_tspec ENUM), or it
         * is an integer type (typically INT) that has been implicitly
         * converted from an enum type.  In both cases, u.enumer is valid.
         */
        bool    t_is_enum:1;
        bool    t_packed:1;
        union {
                int             dimension;      /* if ARRAY */
                struct_or_union *sou;
                enumeration     *enumer;
                sym_t           *params;        /* if t_proto */
        } u;
        unsigned int    t_bit_field_width:8;
        unsigned int    t_bit_field_offset:24;
        struct lint1_type *t_subt;      /*- element type (if ARRAY),
                                         * return value (if FUNC),
                                         * target type (if PTR) */
};

typedef enum {
        SK_VCFT,                /* variable, constant, function, type */
        SK_MEMBER,              /* member of a struct or union */
        SK_TAG,
        SK_LABEL
} symbol_kind;

/* storage classes and related things */
typedef enum {
        NO_SCL,
        EXTERN,                 /* external symbols (independent of decl_t) */
        STATIC,                 /* static symbols (local and global) */
        AUTO,                   /* automatic symbols (except register) */
        REG,                    /* register */
        TYPEDEF,
        THREAD_LOCAL,
        STRUCT_TAG,
        UNION_TAG,
        ENUM_TAG,
        STRUCT_MEMBER,
        UNION_MEMBER,
        BOOL_CONST,
        ENUM_CONST,
        ABSTRACT,               /* abstract symbol (sizeof, casts, unnamed
                                 * argument) */
} scl_t;

/* C23 6.7.4 */
typedef enum {
        FS_INLINE,              /* since C99 */
        FS_NORETURN,            /* since C11 */
} function_specifier;

typedef enum {
        NC_FALSE,               /* since C23 */
        NC_TRUE,                /* since C23 */
        NC_NULLPTR,             /* since C23 */
} named_constant;

/* A type, variable, keyword; basically anything that has a name. */
struct sym {
        const char *s_name;
        const char *s_rename;   /* renamed symbol's given name */
        pos_t   s_def_pos;      /* position of last (prototype) definition,
                                 * prototype declaration, no-prototype-def.,
                                 * tentative definition or declaration, in this
                                 * order */
        pos_t   s_set_pos;      /* position of first initialization */
        pos_t   s_use_pos;      /* position of first use */
        symbol_kind s_kind;
        const struct keyword *s_keyword;
        bool    s_bitfield:1;
        bool    s_set:1;
        bool    s_used:1;
        bool    s_param:1;
        bool    s_register:1;
        bool    s_defparam:1;   /* undefined symbol in old-style function
                                 * definition */
        bool    s_return_type_implicit_int:1;
        bool    s_osdef:1;      /* symbol stems from old-style function def. */
        bool    s_inline:1;
        sym_t   *s_ext_sym;     /* for locally declared external symbols, the
                                 * pointer to the external symbol with the same
                                 * name */
        def_t   s_def;          /* declared, tentative defined, defined */
        scl_t   s_scl;
        int     s_block_level;  /* level of declaration, -1 if not in symbol
                                 * table */
        type_t  *s_type;
        union {
                bool s_bool_constant;
                int s_enum_constant;
                struct {
                        struct_or_union *sm_containing_type;
                        unsigned int sm_offset_in_bits;
                } s_member;
                struct {
                        int sk_token;
                        union {
                                /* if T_TYPE or T_STRUCT_OR_UNION */
                                tspec_t sk_tspec;
                                /* if T_QUAL */
                                type_qualifiers sk_type_qualifier;
                                /* if T_FUNCTION_SPECIFIER */
                                function_specifier function_specifier;
                                /* if T_CON */
                                named_constant named_constant;
                        } u;
                } s_keyword;
                sym_t   *s_old_style_params;    /* parameters in an old-style
                                                 * function definition */
                /*
                 * Only for string initializers with automatic size.
                 * Does not take '\0' characters in the middle into account.
                 */
                size_t s_array_nonnull_dimension;
        } u;
        sym_t   *s_symtab_next; /* next symbol in the same symtab bucket */
        sym_t   **s_symtab_ref; /* pointer to s_symtab_next of the previous
                                 * symbol */
        sym_t   *s_next;        /* next struct/union member, enumerator,
                                 * parameter */
        sym_t   *s_level_next;  /* next symbol declared on the same level */
};

/*
 * Used to keep some information about symbols before they are entered
 * into the symbol table.
 */
typedef struct {
        const char *sb_name;            /* name of symbol */
        size_t  sb_len;                 /* length (without '\0') */
        sym_t   *sb_sym;                /* symbol table entry */
} sbuf_t;


typedef struct {
        struct tnode *func;
        struct tnode **args;
        size_t args_len;
        size_t args_cap;
} function_call;

typedef struct tnode tnode_t;

/* An expression, forming an abstract syntax tree. */
struct tnode {
        op_t    tn_op;
        type_t  *tn_type;
        bool    tn_lvalue:1;
        bool    tn_cast:1;      /* if tn_op == CVT, it's an explicit cast */
        bool    tn_parenthesized:1;
        bool    tn_sys:1;       /* comes from a system header */
        bool    tn_system_dependent:1; /* depends on sizeof or offsetof */
        union {
                struct {
                        tnode_t *left;  /* (left) operand */
                        tnode_t *right; /* right operand */
                } ops;
                sym_t   *sym;           /* if NAME */
                val_t   value;          /* if CON */
                buffer  *str_literals;  /* if STRING; for
                                         * character strings, 'data' points to
                                         * the concatenated string literals in
                                         * source form, and 'len' is the
                                         * length of the concatenation; for
                                         * wide strings, 'data' is NULL and
                                         * 'len' is the number of resulting
                                         * characters */
                function_call *call;    /* if CALL */
        } u;
};

struct generic_selection {
        const type_t *expr_type;
        tnode_t *fallback;
        tnode_t *matched;
        bool assoc_matched;
};

typedef struct {
        bool has_dim;
        int dim;
} array_size;

typedef enum decl_level_kind {
        DLK_EXTERN,             /* global types, variables or functions */
        DLK_STRUCT,             /* struct members */
        DLK_UNION,              /* union members */
        DLK_ENUM,               /* enum constants */
        DLK_OLD_STYLE_PARAMS,   /* parameters in an old-style function
                                 * definition */
        DLK_PROTO_PARAMS,       /* parameters in a prototype function
                                 * definition */
        DLK_AUTO,               /* local types or variables */
        DLK_ABSTRACT            /* abstract (unnamed) declaration; type name;
                                 * used in casts and sizeof */
} decl_level_kind;

/*
 * A declaration level collects information for a declarator in a struct,
 * union or enum declaration, a parameter declaration list, or a plain
 * declaration in or outside a function body.
 *
 * For nested declarations, the global 'dcs' holds all information needed for
 * the current level, the outer levels are available via 'd_enclosing'.
 */
typedef struct decl_level {
        decl_level_kind d_kind;
        tspec_t d_abstract_type;/* VOID, BOOL, CHAR, INT or COMPLEX */
        tspec_t d_complex_mod;  /* FLOAT or DOUBLE */
        tspec_t d_sign_mod;     /* SIGNED or UNSIGN */
        tspec_t d_rank_mod;     /* SHORT, LONG or LLONG */
        scl_t   d_scl;          /* storage class */
        type_t  *d_type;        /* after dcs_end_type, the pointer to the type
                                 * used for all declarators */
        sym_t   *d_redeclared_symbol;
        unsigned int d_sou_size_in_bits;        /* size of the structure or
                                                 * union being built, without
                                                 * trailing padding */
        unsigned int d_sou_align;       /* alignment of the structure
                                         * or union being built */
        unsigned int d_mem_align;       /* alignment of the structure
                                         * or union member */
        type_qualifiers d_qual; /* in declaration specifiers */
        bool    d_inline:1;     /* inline in declaration specifiers */
        bool    d_multiple_storage_classes:1; /* reported in dcs_end_type */
        bool    d_invalid_type_combination:1;
        bool    d_nonempty_decl:1; /* in a function declaration, whether at
                                 * least one tag was declared */
        bool    d_no_type_specifier:1;
        bool    d_asm:1;        /* set if d_ctx == AUTO and asm() present */
        bool    d_packed:1;
        bool    d_used:1;
        bool    d_noreturn:1;   /* function never returns normally */
        type_t  *d_tag_type;    /* during a member or enumerator declaration,
                                 * the tag type to which the member belongs */
        sym_t   *d_func_params; /* during a function declaration, the
                                 * parameters, stored in the enclosing level */
        pos_t   d_func_def_pos; /* position of the function definition */
        sym_t   *d_first_dlsym; /* first symbol declared at this level */
        sym_t   **d_last_dlsym; /* points to s_level_next in the last symbol
                                   declaration at this level */
        sym_t   *d_func_proto_syms;     /* symbols defined in prototype, such
                                         * as tagged types or parameter names,
                                         * may overlap d_func_params */
        bool    d_finished;     /* the declaration-specifiers are complete,
                                 * the declarator is being parsed */
        struct decl_level *d_enclosing; /* the enclosing declaration level */
} decl_level;

typedef struct {
        sym_t   *first;
        bool    vararg:1;
        bool    prototype:1;
        bool    used:1;
        bool    noreturn:1;
        bool    identifier:1;
} parameter_list;

/*
 * A sequence of asterisks and qualifiers, from right to left.  For example,
 * 'const ***volatile **const volatile' results in [c-v-, ----, --v-, ----,
 * ----].  The leftmost 'const' is not included in this list, it is stored in
 * dcs->d_qual instead.
 */
typedef struct qual_ptr {
        type_qualifiers qualifiers;
        struct qual_ptr *p_next;
} qual_ptr;

/* The values of the 'case' labels. */
typedef struct {
        val_t   *vals;
        size_t  len;
        size_t  cap;
} case_labels;

typedef enum {
        CS_DO_WHILE,
        CS_FOR,
        CS_FUNCTION_BODY,
        CS_IF,
        CS_SWITCH,
        CS_WHILE
} control_statement_kind;

/*
 * Used to keep information about nested control statements.
 */
typedef struct control_statement {
        control_statement_kind c_kind;  /* to ensure proper nesting */
        bool    c_loop:1;       /* 'continue' and 'break' are valid */
        bool    c_switch:1;     /* 'case' and 'break' are valid */
        bool    c_break:1;      /* the loop/switch has a reachable 'break'
                                 * statement */
        bool    c_continue:1;   /* the loop has a reachable 'continue'
                                 * statement */
        bool    c_default:1;    /* the switch has a 'default' label */
        bool    c_maybe_endless:1;      /* the controlling expression is
                                         * always true (as in 'for (;;)' or
                                         * 'while (1)'), there may be break
                                         * statements though */
        bool    c_always_then:1;
        bool    c_reached_end_of_then:1;
        bool    c_had_return_noval:1;   /* had "return;" */
        bool    c_had_return_value:1;   /* had "return expr;" */

        type_t  *c_switch_type; /* type of switch expression */
        tnode_t *c_switch_expr;
        case_labels c_case_labels;      /* list of case values */

        memory_pool c_for_expr3_mem;    /* saved memory for end of loop
                                         * expression in for() */
        tnode_t *c_for_expr3;   /* end of loop expr in for() */
        pos_t   c_for_expr3_pos;        /* position of end of loop expr */
        pos_t   c_for_expr3_csrc_pos;   /* same for csrc_pos */

        struct control_statement *c_surrounding;
} control_statement;

typedef struct {
        size_t lo;              /* inclusive */
        size_t hi;              /* inclusive */
} range_t;

typedef enum designator_kind {
        DK_MEMBER,              /* .member */
        DK_SUBSCRIPT,           /* [subscript] */
        DK_SCALAR               /* no textual representation, not generated by
                                 * the parser; used for scalar initializer
                                 * expressions surrounded by braces */
} designator_kind;

/*
 * A single component on the path from the "current object" of a brace level
 * to the sub-object that is initialized by an expression.
 *
 * C99 6.7.8p6, 6.7.8p7
 */
typedef struct designator {
        designator_kind dr_kind;
        const sym_t     *dr_member;     /* for DK_MEMBER */
        size_t          dr_subscript;   /* for DK_SUBSCRIPT */
        bool            dr_done;
} designator;

/*
 * The path from the "current object" of a brace level to the sub-object that
 * is initialized by an expression.  Examples of designations are '.member'
 * or '.member[123].member.member[1][1]'.
 *
 * C99 6.7.8p6, 6.7.8p7
 */
typedef struct designation {
        designator      *dn_items;
        size_t          dn_len;
        size_t          dn_cap;
} designation;

typedef enum {
        LC_ARGSUSED,
        LC_BITFIELDTYPE,
        LC_FALLTHROUGH,
        LC_LINTLIBRARY,
        LC_LINTED,
        LC_LONGLONG,
        LC_NOTREACHED,
        LC_PRINTFLIKE,
        LC_PROTOLIB,
        LC_SCANFLIKE,
        LC_VARARGS,
} lint_comment;

typedef struct {
        size_t start;
        size_t end;
        uint64_t value;
        bool escaped;           /* \n, \003, \x24 */
        bool named_escape;      /* \a, \n, etc. */
        bool literal_escape;    /* \?, \\, etc. */
        uint8_t octal_digits;   /* 1 to 3; 0 means not applicable */
        uint8_t hex_digits;     /* 1 to 3; 0 means not applicable */
        bool next_literal;      /* when a new string literal begins */
        bool invalid_escape;    /* single-character escape, recoverable */
        bool overflow;          /* for octal and hex escapes */
        bool missing_hex_digits;
        bool unescaped_newline; /* stops iterating */
} quoted_iterator;

typedef enum {
        TK_IDENTIFIER,
        TK_CONSTANT,
        TK_STRING_LITERALS,
        TK_PUNCTUATOR,
} token_kind;

typedef struct token {
        token_kind kind;
        union {
                const char *identifier;
                val_t constant;
                buffer string_literals;
                const char *punctuator;
        } u;
} token;

typedef struct balanced_token_sequence balanced_token_sequence;
typedef struct balanced_token balanced_token;

struct balanced_token_sequence {
        balanced_token *tokens;
        size_t len;
        size_t cap;
};

struct balanced_token {
        char kind;      // '\0', '(', '[', '{'
        union {
                token token;
                balanced_token_sequence tokens;
        } u;
};

typedef struct {
        const char *prefix;
        const char *name;
        balanced_token_sequence *arg;
} attribute;

typedef struct {
        attribute *attrs;
        size_t len;
        size_t cap;
} attribute_list;

#include "externs1.h"

#define lint_assert(cond)                                               \
        do {                                                            \
                if (!(cond))                                            \
                        assert_failed(__FILE__, __LINE__, __func__, #cond); \
        } while (false)

#ifdef DEBUG
#  include "err-msgs.h"

/* ARGSUSED */
static inline void __printflike(1, 2)
check_printf(const char *fmt, ...)
{
}

#  define wrap_check_printf_at(func, msgid, pos, args...)               \
        do {                                                            \
                check_printf(__CONCAT(MSG_, msgid), ##args);            \
                (func)(msgid, pos, ##args);                             \
        } while (false)

#  define error_at(msgid, pos, args...) \
        wrap_check_printf_at(error_at, msgid, pos, ##args)
#  define warning_at(msgid, pos, args...) \
        wrap_check_printf_at(warning_at, msgid, pos, ##args)
#  define message_at(msgid, pos, args...) \
        wrap_check_printf_at(message_at, msgid, pos, ##args)

#  define wrap_check_printf(func, cond, msgid, args...)                 \
        ({                                                              \
                if (cond)                                               \
                        debug_step("%s:%d: %s %d '%s' in %s",           \
                            __FILE__, __LINE__, #func, msgid,           \
                            __CONCAT(MSG_, msgid), __func__);           \
                check_printf(__CONCAT(MSG_, msgid), ##args);            \
                (func)(msgid, ##args);                                  \
                /* LINTED 129 */                                        \
        })

#  define error(msgid, args...) wrap_check_printf(error, \
    true, msgid, ##args)
#  define warning(msgid, args...) wrap_check_printf(warning, \
    true, msgid, ##args)
#  define gnuism(msgid, args...) wrap_check_printf(gnuism, \
    !allow_gcc || (!allow_trad && !allow_c99), msgid, ##args)
#  define c99ism(msgid, args...) wrap_check_printf(c99ism, \
    !allow_c99 && (!allow_gcc || !allow_trad), msgid, ##args)
#  define c11ism(msgid, args...) wrap_check_printf(c11ism, \
    !allow_c11 && !allow_gcc, msgid, ##args)
#  define c23ism(msgid, args...) wrap_check_printf(c23ism, \
    !allow_c23, msgid, ##args)
#endif

#ifdef DEBUG
#  define query_message(query_id, args...)                              \
        do {                                                            \
                debug_step("%s:%d: query %d '%s' in %s",                \
                    __FILE__, __LINE__,                                 \
                    query_id, __CONCAT(MSG_Q, query_id), __func__);     \
                check_printf(__CONCAT(MSG_Q, query_id), ##args);        \
                (query_message)(query_id, ##args);                      \
        } while (false)
#else
#  define query_message(...)                                            \
        do {                                                            \
                if (any_query_enabled)                                  \
                        (query_message)(__VA_ARGS__);                   \
        } while (false)
#endif

/* Copies curr_pos, keeping things unique. */
static inline pos_t
unique_curr_pos(void)
{
        pos_t curr = curr_pos;
        curr_pos.p_uniq++;
        if (curr_pos.p_file == csrc_pos.p_file)
                csrc_pos.p_uniq++;
        return curr;
}

static inline bool
is_nonzero_val(const val_t *val)
{
        return is_floating(val->v_tspec)
            ? val->u.floating != 0.0
            : val->u.integer != 0;
}

static inline bool
constant_is_nonzero(const tnode_t *tn)
{
        lint_assert(tn->tn_op == CON);
        lint_assert(tn->tn_type->t_tspec == tn->u.value.v_tspec);
        return is_nonzero_val(&tn->u.value);
}

static inline bool
is_zero(const tnode_t *tn)
{
        return tn != NULL && tn->tn_op == CON && !is_nonzero_val(&tn->u.value);
}

static inline bool
is_nonzero(const tnode_t *tn)
{
        return tn != NULL && tn->tn_op == CON && is_nonzero_val(&tn->u.value);
}

static inline const char *
op_name(op_t op)
{
        return modtab[op].m_name;
}

static inline bool
is_binary(const tnode_t *tn)
{
        return modtab[tn->tn_op].m_binary;
}

static inline uint64_t
bit(unsigned i)
{
        /*
         * TODO: Add proper support for INT128. This involves changing val_t to
         * 128 bits.
         */
        if (i >= 64)
                return 0;       /* XXX: not correct for INT128 and UINT128 */

        lint_assert(i < 64);
        return (uint64_t)1 << i;
}

static inline bool
msb(int64_t si, tspec_t t)
{
        return ((uint64_t)si & bit(size_in_bits(t) - 1)) != 0;
}

static inline uint64_t
value_bits(unsigned bitsize)
{
        lint_assert(bitsize > 0);

        /* for long double (80 or 128), double _Complex (128) */
        /*
         * XXX: double _Complex does not have 128 bits of precision, therefore
         * it should never be necessary to query the value bits of such a type;
         * see d_c99_complex_split.c to trigger this case.
         */
        if (bitsize >= 64)
                return ~(uint64_t)0;

        return ~(~(uint64_t)0 << bitsize);
}

/* C99 6.7.8p7 */
static inline bool
is_struct_or_union(tspec_t t)
{
        return t == STRUCT || t == UNION;
}

static inline bool
is_member(const sym_t *sym)
{
        return sym->s_scl == STRUCT_MEMBER || sym->s_scl == UNION_MEMBER;
}

static inline void
set_sym_kind(symbol_kind kind)
{
        if (yflag)
                debug_step("%s: %s -> %s", __func__,
                    symbol_kind_name(sym_kind), symbol_kind_name(kind));
        sym_kind = kind;
}

static inline type_attributes
merge_type_attributes(type_attributes a, type_attributes b)
{
        return (type_attributes){
                .used = a.used || b.used,
                .noreturn = a.noreturn || b.noreturn,
                .bit_width = b.bit_width > 0 ? b.bit_width : a.bit_width,
        };
}

static inline type_attributes
no_type_attributes(void)
{
        return (type_attributes){ .used = false };
}