/* $NetBSD: init.c,v 1.271 2024/11/13 04:32:49 rillig Exp $ */ /* * Copyright (c) 1994, 1995 Jochen Pohl * Copyright (c) 2021 Roland Illig * 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) __RCSID("$NetBSD: init.c,v 1.271 2024/11/13 04:32:49 rillig Exp $"); #endif #include #include #include "lint1.h" /* * Initialization of global or local objects, like in: * * int number = 12345; * int number_with_braces = { 12345 }; * int array_of_unknown_size[] = { 111, 222, 333 }; * struct { int x, y; } point = { .y = 4, .x = 3 }; * * During an initialization, the grammar parser calls these functions: * * begin_initialization * init_lbrace for each '{' * add_designator_member for each '.member' before '=' * add_designator_subscript for each '[123]' before '=' * init_expr for each expression * init_rbrace for each '}' * end_initialization * * Each '{' begins a new brace level, each '}' ends the current brace level. * Each brace level has an associated "current object", which is the starting * point for resolving the optional designations such as '.member[3]'. * * See also: * C99 6.7.8 "Initialization" * C11 6.7.9 "Initialization" * d_c99_init.c for more examples */ /* * Everything that happens between a '{' and the corresponding '}', as part * of an initialization. * * Each brace level has a "current object". For the outermost brace level, * it is the same as the object to be initialized. Each nested '{' begins a * nested brace level, for the sub-object pointed to by the designator of the * outer brace level. * * C99 6.7.8p17 */ typedef struct brace_level { /* The type of the "current object". */ const type_t *bl_type; /* * The path from the "current object" to the sub-object that is * initialized by the next expression. * * Initially, the designation is empty. Before handling an expression, * the designation is updated to point to the corresponding sub-object * to be initialized. After handling an expression, the designation is * marked as done. It is later advanced as necessary. */ designation bl_designation; struct brace_level *bl_enclosing; } brace_level; /* An ongoing initialization. */ typedef struct initialization { /* The symbol that is to be initialized. */ sym_t *in_sym; /* The innermost brace level. */ brace_level *in_brace_level; /* * The maximum subscript that has ever been seen for an array of * unknown size, which can only occur at the outermost brace level. */ size_t in_max_subscript; /* * Is set when a structural error occurred in the initialization. If * set, the rest of the initialization is still parsed, but the * initialization assignments are not checked. */ bool in_err; struct initialization *in_enclosing; } initialization; static void * unconst_cast(const void *p) { void *r; memcpy(&r, &p, sizeof(r)); return r; } static bool has_automatic_storage_duration(const sym_t *sym) { return sym->s_scl == AUTO || sym->s_scl == REG; } /* * Test whether rn is a string literal that can initialize ltp. * * See also: * C99 6.7.8p14 for plain character strings * C99 6.7.8p15 for wide character strings */ static bool can_init_character_array(const type_t *ltp, const tnode_t *rn) { if (!(ltp != NULL && ltp->t_tspec == ARRAY && rn->tn_op == STRING)) return false; tspec_t lst = ltp->t_subt->t_tspec; tspec_t rst = rn->tn_type->t_subt->t_tspec; return rst == CHAR ? lst == CHAR || lst == UCHAR || lst == SCHAR : lst == WCHAR_TSPEC; } static const sym_t * skip_unnamed(const sym_t *m) { while (m != NULL && m->s_name == unnamed && !is_struct_or_union(m->s_type->t_tspec)) m = m->s_next; return m; } static const sym_t * first_named_member(const type_t *tp) { lint_assert(is_struct_or_union(tp->t_tspec)); return skip_unnamed(tp->u.sou->sou_first_member); } static void update_type_of_array_of_unknown_size(sym_t *sym, size_t size) { type_t *tp = block_dup_type(sym->s_type); tp->u.dimension = (int)size; tp->t_incomplete_array = false; sym->s_type = tp; debug_step("completed array type is '%s'", type_name(sym->s_type)); outsym(sym, sym->s_scl, sym->s_def); } static void check_bit_field_init(const tnode_t *ln, tspec_t lt, tspec_t rt) { if (!allow_c90 && is_integer(lt) && ln->tn_type->t_bitfield && !is_integer(rt)) /* bit-field initializer must be an integer in ... */ warning(186); } static void check_non_constant_initializer(const tnode_t *tn, const sym_t *sym) { if (tn == NULL || tn->tn_op == CON) return; const sym_t *unused_sym; ptrdiff_t unused_offs; if (constant_addr(tn, &unused_sym, &unused_offs)) return; if (has_automatic_storage_duration(sym)) { /* non-constant initializer */ c99ism(177); } else { /* non-constant initializer */ error(177); } } static void check_trad_no_auto_aggregate(const sym_t *sym) { if (has_automatic_storage_duration(sym) && !is_scalar(sym->s_type->t_tspec)) { /* no automatic aggregate initialization in traditional C */ warning(188); } } static void check_init_expr(const type_t *ltp, sym_t *lsym, tnode_t *rn) { type_t *lutp = expr_unqualified_type(ltp); /* Create a temporary node for the left side. */ tnode_t *ln = expr_zero_alloc(sizeof(*ln), "tnode"); ln->tn_op = NAME; ln->tn_type = lutp; ln->tn_lvalue = true; ln->u.sym = lsym; rn = cconv(rn); debug_step("typeok '%s', '%s'", type_name(ln->tn_type), type_name(rn->tn_type)); if (!typeok(INIT, 0, ln, rn)) return; memory_pool saved_mem = expr_save_memory(); expr(rn, true, false, true, false, "init"); expr_restore_memory(saved_mem); tspec_t lt = ln->tn_type->t_tspec; tspec_t rt = rn->tn_type->t_tspec; check_bit_field_init(ln, lt, rt); if (lt != rt || (ltp->t_bitfield && rn->tn_op == CON)) rn = convert(INIT, 0, unconst_cast(ltp), rn); check_non_constant_initializer(rn, lsym); } static const type_t * designator_type(const designator *dr, const type_t *tp) { switch (tp->t_tspec) { case STRUCT: case UNION: if (dr->dr_kind != DK_MEMBER) { const sym_t *fmem = first_named_member(tp); /* syntax error '%s' */ error(249, "designator '[...]' is only for arrays"); return fmem != NULL ? fmem->s_type : NULL; } lint_assert(dr->dr_member != NULL); return dr->dr_member->s_type; case ARRAY: if (dr->dr_kind != DK_SUBSCRIPT) { /* syntax error '%s' */ error(249, "designator '.member' is only for struct/union"); } if (!tp->t_incomplete_array) lint_assert( dr->dr_subscript < (size_t)tp->u.dimension); return tp->t_subt; default: if (dr->dr_kind != DK_SCALAR) /* syntax error '%s' */ error(249, "scalar type cannot use designator"); return tp; } } #ifdef DEBUG static void designator_debug(const designator *dr) { if (dr->dr_kind == DK_MEMBER) { lint_assert(dr->dr_subscript == 0); debug_printf(".%s", dr->dr_member != NULL ? dr->dr_member->s_name : ""); } else if (dr->dr_kind == DK_SUBSCRIPT) { lint_assert(dr->dr_member == NULL); debug_printf("[%zu]", dr->dr_subscript); } else { lint_assert(dr->dr_member == NULL); lint_assert(dr->dr_subscript == 0); debug_printf(""); } if (dr->dr_done) debug_printf(" (done)"); } static void designation_debug(const designation *dn) { if (dn->dn_len == 0) { debug_step("designation: (empty)"); return; } debug_printf("designation: "); for (size_t i = 0; i < dn->dn_len; i++) designator_debug(dn->dn_items + i); debug_printf("\n"); } #else #define designation_debug(dn) do { } while (false) #endif static designator * designation_last(designation *dn) { lint_assert(dn->dn_len > 0); return &dn->dn_items[dn->dn_len - 1]; } void designation_push(designation *dn, designator_kind kind, const sym_t *member, size_t subscript) { if (dn->dn_len == dn->dn_cap) { dn->dn_cap += 4; dn->dn_items = xrealloc(dn->dn_items, dn->dn_cap * sizeof(dn->dn_items[0])); } designator *dr = &dn->dn_items[dn->dn_len++]; dr->dr_kind = kind; dr->dr_member = member; dr->dr_subscript = subscript; dr->dr_done = false; designation_debug(dn); } /* * Extend the designation as appropriate for the given type. * * C11 6.7.9p17 */ static bool designation_descend(designation *dn, const type_t *tp) { if (is_struct_or_union(tp->t_tspec)) { const sym_t *member = first_named_member(tp); if (member == NULL) return false; designation_push(dn, DK_MEMBER, member, 0); } else if (tp->t_tspec == ARRAY) designation_push(dn, DK_SUBSCRIPT, NULL, 0); else designation_push(dn, DK_SCALAR, NULL, 0); return true; } /* * Starting at the type of the current object, resolve the type of the * sub-object by following each designator in the list. * * C99 6.7.8p18 */ static const type_t * designation_type(const designation *dn, const type_t *tp) { for (size_t i = 0; i < dn->dn_len && tp != NULL; i++) tp = designator_type(dn->dn_items + i, tp); return tp; } static const type_t * designation_parent_type(const designation *dn, const type_t *tp) { for (size_t i = 0; i + 1 < dn->dn_len && tp != NULL; i++) tp = designator_type(dn->dn_items + i, tp); return tp; } static brace_level * brace_level_new(const type_t *tp, brace_level *enclosing) { brace_level *bl = xcalloc(1, sizeof(*bl)); bl->bl_type = tp; bl->bl_enclosing = enclosing; return bl; } static void brace_level_free(brace_level *bl) { free(bl->bl_designation.dn_items); free(bl); } #ifdef DEBUG static void brace_level_debug(const brace_level *bl) { lint_assert(bl->bl_type != NULL); debug_printf("type '%s'\n", type_name(bl->bl_type)); debug_indent_inc(); designation_debug(&bl->bl_designation); debug_indent_dec(); } #else #define brace_level_debug(level) do { } while (false) #endif /* Return the type of the sub-object that is currently being initialized. */ static const type_t * brace_level_sub_type(const brace_level *bl) { return designation_type(&bl->bl_designation, bl->bl_type); } /* * After initializing a sub-object, advance the designation to point after * the sub-object that has just been initialized. * * C99 6.7.8p17 * C11 6.7.9p17 */ static void brace_level_advance(brace_level *bl, size_t *max_subscript) { debug_enter(); designation *dn = &bl->bl_designation; const type_t *tp = designation_parent_type(dn, bl->bl_type); if (bl->bl_designation.dn_len == 0) (void)designation_descend(dn, bl->bl_type); designator *dr = designation_last(dn); switch (tp->t_tspec) { case STRUCT: lint_assert(dr->dr_member != NULL); dr->dr_member = skip_unnamed(dr->dr_member->s_next); if (dr->dr_member == NULL) dr->dr_done = true; break; case UNION: dr->dr_member = NULL; dr->dr_done = true; break; case ARRAY: dr->dr_subscript++; if (tp->t_incomplete_array && dr->dr_subscript > *max_subscript) *max_subscript = dr->dr_subscript; if (!tp->t_incomplete_array && dr->dr_subscript >= (size_t)tp->u.dimension) dr->dr_done = true; break; default: dr->dr_done = true; break; } designation_debug(dn); debug_leave(); } static void warn_too_many_initializers(designator_kind kind, const type_t *tp) { if (kind == DK_MEMBER) /* too many struct/union initializers */ error(172); else if (kind == DK_SUBSCRIPT) { lint_assert(tp->t_tspec == ARRAY); lint_assert(!tp->t_incomplete_array); /* too many array initializers, expected %d */ error(173, tp->u.dimension); } else /* too many initializers for '%s' */ error(174, type_name(tp)); } static bool brace_level_pop_done(brace_level *bl, size_t *max_subscript) { designation *dn = &bl->bl_designation; designator_kind dr_kind = designation_last(dn)->dr_kind; const type_t *sub_type = designation_parent_type(dn, bl->bl_type); while (designation_last(dn)->dr_done) { dn->dn_len--; designation_debug(dn); if (dn->dn_len == 0) { warn_too_many_initializers(dr_kind, sub_type); return false; } brace_level_advance(bl, max_subscript); } return true; } static void brace_level_pop_final(brace_level *bl, size_t *max_subscript) { designation *dn = &bl->bl_designation; while (dn->dn_len > 0 && designation_last(dn)->dr_done) { dn->dn_len--; designation_debug(dn); if (dn->dn_len == 0) return; brace_level_advance(bl, max_subscript); } } /* Make the designation point to the sub-object to be initialized next. */ static bool brace_level_goto(brace_level *bl, const tnode_t *rn, size_t *max_subscript) { designation *dn = &bl->bl_designation; if (dn->dn_len == 0 && can_init_character_array(bl->bl_type, rn)) return true; if (dn->dn_len == 0 && !designation_descend(dn, bl->bl_type)) return false; again: if (!brace_level_pop_done(bl, max_subscript)) return false; const type_t *ltp = brace_level_sub_type(bl); if (types_compatible(ltp, rn->tn_type, true, false, NULL)) return true; if (is_struct_or_union(ltp->t_tspec) || ltp->t_tspec == ARRAY) { if (can_init_character_array(ltp, rn)) return true; if (!designation_descend(dn, ltp)) return false; goto again; } return true; } static initialization * initialization_new(sym_t *sym, initialization *enclosing) { initialization *in = xcalloc(1, sizeof(*in)); in->in_sym = sym; in->in_enclosing = enclosing; return in; } static void initialization_free(initialization *in) { brace_level *bl, *next; for (bl = in->in_brace_level; bl != NULL; bl = next) { next = bl->bl_enclosing; brace_level_free(bl); } free(in); } #ifdef DEBUG static void initialization_debug(const initialization *in) { if (in->in_err) debug_step("initialization error"); if (in->in_brace_level == NULL) { debug_step("no brace level"); return; } const brace_level *bl; size_t i = 0; for (bl = in->in_brace_level; bl != NULL; bl = bl->bl_enclosing) { debug_printf("brace level %zu: ", i); brace_level_debug(bl); i++; } } #else #define initialization_debug(in) do { } while (false) #endif /* * Return the type of the object or sub-object that is currently being * initialized. */ static const type_t * initialization_sub_type(initialization *in) { if (in->in_brace_level == NULL) return in->in_sym->s_type; const type_t *tp = brace_level_sub_type(in->in_brace_level); if (tp == NULL) in->in_err = true; return tp; } static void initialization_lbrace(initialization *in) { if (in->in_err) return; debug_enter(); const type_t *tp = initialization_sub_type(in); if (tp == NULL) goto done; brace_level *outer_bl = in->in_brace_level; if (!allow_c90 && outer_bl == NULL) check_trad_no_auto_aggregate(in->in_sym); if (!allow_c90 && tp->t_tspec == UNION) /* initialization of union is illegal in traditional C */ warning(238); if (is_struct_or_union(tp->t_tspec) && tp->u.sou->sou_incomplete) { /* initialization of incomplete type '%s' */ error(175, type_name(tp)); in->in_err = true; goto done; } if (outer_bl != NULL && outer_bl->bl_designation.dn_len == 0) { designation *dn = &outer_bl->bl_designation; (void)designation_descend(dn, outer_bl->bl_type); tp = designation_type(dn, outer_bl->bl_type); } in->in_brace_level = brace_level_new(tp, outer_bl); if (is_struct_or_union(tp->t_tspec) && first_named_member(tp) == NULL) { /* cannot initialize struct/union with no named member */ error(179); in->in_err = true; } done: initialization_debug(in); debug_leave(); } static void initialization_rbrace(initialization *in) { debug_enter(); if (in->in_brace_level != NULL) brace_level_pop_final(in->in_brace_level, &in->in_max_subscript); /* C99 6.7.8p22 */ if (in->in_sym->s_type->t_incomplete_array && in->in_brace_level->bl_enclosing == NULL) { /* prevent "empty array declaration for '%s' [190]" */ size_t dim = in->in_max_subscript; if (dim == 0 && in->in_err) dim = 1; update_type_of_array_of_unknown_size(in->in_sym, dim); } if (in->in_err) goto done; brace_level *inner_bl = in->in_brace_level; brace_level *outer_bl = inner_bl->bl_enclosing; in->in_brace_level = outer_bl; brace_level_free(inner_bl); if (outer_bl != NULL) brace_level_advance(outer_bl, &in->in_max_subscript); done: initialization_debug(in); debug_leave(); } static void initialization_add_designator_member(initialization *in, const char *name) { if (in->in_err) return; brace_level *bl = in->in_brace_level; lint_assert(bl != NULL); const type_t *tp = brace_level_sub_type(bl); if (is_struct_or_union(tp->t_tspec)) goto proceed; else if (tp->t_tspec == ARRAY) /* syntax error '%s' */ error(249, "designator '.member' is only for struct/union"); else /* syntax error '%s' */ error(249, "scalar type cannot use designator"); in->in_err = true; return; proceed:; const sym_t *member = find_member(tp->u.sou, name); if (member == NULL) { /* type '%s' does not have member '%s' */ error(101, type_name(tp), name); in->in_err = true; return; } designation_push(&bl->bl_designation, DK_MEMBER, member, 0); } static void initialization_add_designator_subscript(initialization *in, size_t subscript) { if (in->in_err) return; brace_level *bl = in->in_brace_level; lint_assert(bl != NULL); const type_t *tp = brace_level_sub_type(bl); if (tp->t_tspec != ARRAY) { /* syntax error '%s' */ error(249, "designator '[...]' is only for arrays"); in->in_err = true; return; } if (!tp->t_incomplete_array && subscript >= (size_t)tp->u.dimension) { /* array subscript %ju cannot be > %d */ error(168, (uintmax_t)subscript, tp->u.dimension - 1); subscript = 0; /* suppress further errors */ } if (tp->t_incomplete_array && subscript > in->in_max_subscript) in->in_max_subscript = subscript; designation_push(&bl->bl_designation, DK_SUBSCRIPT, NULL, subscript); } /* * Initialize an object with automatic storage duration that has an * initializer expression without braces. */ static bool initialization_expr_using_op(initialization *in, tnode_t *rn) { if (!has_automatic_storage_duration(in->in_sym)) return false; if (in->in_brace_level != NULL) return false; if (in->in_sym->s_type->t_tspec == ARRAY) return false; debug_step("handing over to INIT"); tnode_t *ln = build_name(in->in_sym, false); ln->tn_type = expr_unqualified_type(ln->tn_type); tnode_t *tn = build_binary(ln, INIT, false /* XXX */, rn); expr(tn, false, false, false, false, "init"); return true; } /* Initialize a character array or wchar_t array with a string literal. */ static bool initialization_init_array_from_string(initialization *in, tnode_t *tn) { if (tn->tn_op != STRING) return false; const type_t *tp = initialization_sub_type(in); if (!can_init_character_array(tp, tn)) return false; size_t len = tn->u.str_literals->len; if (tn->u.str_literals->data != NULL) { quoted_iterator it = { .end = 0 }; for (len = 0; quoted_next(tn->u.str_literals, &it); len++) continue; } if (!tp->t_incomplete_array && (size_t)tp->u.dimension < len) /* string literal too long (%ju) for target array (%ju) */ warning(187, (uintmax_t)len, (uintmax_t)tp->u.dimension); brace_level *bl = in->in_brace_level; if (bl != NULL && bl->bl_designation.dn_len == 0) (void)designation_descend(&bl->bl_designation, bl->bl_type); if (bl != NULL) brace_level_advance(bl, &in->in_max_subscript); if (tp->t_incomplete_array) update_type_of_array_of_unknown_size(in->in_sym, len + 1); return true; } /* * Initialize a single sub-object as part of the currently ongoing * initialization. */ static void initialization_expr(initialization *in, tnode_t *tn) { if (in->in_err || tn == NULL) return; debug_enter(); brace_level *bl = in->in_brace_level; if (bl != NULL && !brace_level_goto(bl, tn, &in->in_max_subscript)) { in->in_err = true; goto done; } if (in->in_sym->s_type->t_tspec == AUTO_TYPE) in->in_sym->s_type = block_dup_type(tn->tn_type); if (initialization_expr_using_op(in, tn)) goto done; if (initialization_init_array_from_string(in, tn)) goto done; if (in->in_err) goto done; const type_t *tp = initialization_sub_type(in); if (tp == NULL) goto done; if (bl == NULL && !is_scalar(tp->t_tspec)) { /* {}-enclosed or constant initializer of type '%s' required */ error(181, type_name(in->in_sym->s_type)); goto done; } debug_step("expecting '%s', expression has '%s'", type_name(tp), type_name(tn->tn_type)); check_init_expr(tp, in->in_sym, tn); if (bl != NULL) brace_level_advance(bl, &in->in_max_subscript); done: initialization_debug(in); debug_leave(); } static initialization *init; sym_t * current_initsym(void) { return init->in_sym; } void begin_initialization(sym_t *sym) { debug_step("begin initialization of '%s'", type_name(sym->s_type)); debug_indent_inc(); init = initialization_new(sym, init); } void end_initialization(void) { initialization *in = init; init = in->in_enclosing; initialization_free(in); debug_indent_dec(); debug_step("end initialization"); } void begin_designation(void) { initialization *in = init; if (in->in_err) return; designation *dn = &in->in_brace_level->bl_designation; dn->dn_len = 0; designation_debug(dn); } void add_designator_member(sbuf_t *sb) { initialization_add_designator_member(init, sb->sb_name); } void add_designator_subscript(range_t range) { initialization_add_designator_subscript(init, range.hi); } void init_lbrace(void) { initialization_lbrace(init); } void init_expr(tnode_t *tn) { initialization_expr(init, tn); } void init_rbrace(void) { initialization_rbrace(init); }