diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index bc90be47151..ea0294f0738 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -11183,9 +11183,12 @@ c_parser_generic_selection (c_parser *parser) else { c_inhibit_evaluation_warnings++; + in_generic++; selector = c_parser_expr_no_commas (parser, NULL); selector = default_function_array_conversion (selector_loc, selector); c_inhibit_evaluation_warnings--; + in_generic--; + pop_maybe_used (!flag_isoc23); if (selector.value == error_mark_node) { @@ -11214,6 +11217,7 @@ c_parser_generic_selection (c_parser *parser) } auto_vec associations; + struct maybe_used_decl *maybe_used_default = NULL; while (1) { struct c_generic_association assoc, *iter; @@ -11269,11 +11273,19 @@ c_parser_generic_selection (c_parser *parser) if (!match) c_inhibit_evaluation_warnings++; + in_generic++; assoc.expression = c_parser_expr_no_commas (parser, NULL); if (!match) c_inhibit_evaluation_warnings--; + in_generic--; + if (!match) + pop_maybe_used (!flag_isoc23); + else if (assoc.type == NULL_TREE) + maybe_used_default = save_maybe_used (); + else + pop_maybe_used (true); if (assoc.expression.value == error_mark_node) { @@ -11334,6 +11346,20 @@ c_parser_generic_selection (c_parser *parser) c_parser_consume_token (parser); } + if (match_found >= 0 && matched_assoc.type == NULL_TREE) + { + /* Declarations referenced in the default association are used. */ + restore_maybe_used (maybe_used_default); + pop_maybe_used (true); + } + else if (maybe_used_default) + { + /* Declarations referenced in the default association are not used, but + are treated as used before C23. */ + restore_maybe_used (maybe_used_default); + pop_maybe_used (!flag_isoc23); + } + unsigned int ix; struct c_generic_association *iter; FOR_EACH_VEC_ELT (associations, ix, iter) diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index a06565d1e8b..162add0522a 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -624,6 +624,19 @@ enum c_inline_static_type { csi_modifiable }; +/* Record details of decls possibly used inside sizeof or typeof. */ +struct maybe_used_decl +{ + /* The decl. */ + tree decl; + /* The level seen at (in_sizeof + in_typeof + in_countof + in_generic). */ + int level; + /* Seen in address-of. */ + bool address; + /* The next one at this level or above, or NULL. */ + struct maybe_used_decl *next; +}; + /* in c-parser.cc */ struct c_tree_token_vec; @@ -774,6 +787,7 @@ extern int in_alignof; extern int in_sizeof; extern int in_countof; extern int in_typeof; +extern int in_generic; extern bool c_in_omp_for; extern bool c_omp_array_section_p; @@ -833,6 +847,8 @@ extern tree build_array_ref (location_t, tree, tree); extern tree build_omp_array_section (location_t, tree, tree, tree); extern tree build_external_ref (location_t, tree, bool, tree *); extern void pop_maybe_used (bool); +extern struct maybe_used_decl *save_maybe_used (); +extern void restore_maybe_used (struct maybe_used_decl *); extern void mark_decl_used (tree, bool); extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr); extern struct c_expr c_expr_sizeof_type (location_t, struct c_type_name *); diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 9b2aeea50f4..371583bd64e 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -78,6 +78,9 @@ int in_countof; /* The level of nesting inside "typeof". */ int in_typeof; +/* The level of nesting inside "_Generic". */ +int in_generic; + /* True when parsing OpenMP loop expressions. */ bool c_in_omp_for; @@ -3690,7 +3693,7 @@ mark_decl_used (tree ref, bool address) return; /* If we may be in an unevaluated context, delay the decision. */ - if (in_sizeof || in_typeof || in_countof) + if (in_sizeof || in_typeof || in_countof || in_generic) return record_maybe_used_decl (ref, address); if (static_p) @@ -3842,46 +3845,36 @@ build_external_ref (location_t loc, tree id, bool fun, tree *type) return ref; } -/* Record details of decls possibly used inside sizeof or typeof. */ -struct maybe_used_decl -{ - /* The decl. */ - tree decl; - /* The level seen at (in_sizeof + in_typeof + in_countof). */ - int level; - /* Seen in address-of. */ - bool address; - /* The next one at this level or above, or NULL. */ - struct maybe_used_decl *next; -}; - static struct maybe_used_decl *maybe_used_decls; -/* Record that DECL, a reference seen inside sizeof or typeof, might be used - if the operand of sizeof is a VLA type or the operand of typeof is a variably - modified type. */ +/* Record that DECL, a reference seen inside sizeof or typeof or _Countof or + _Generic, might be used if the operand of sizeof is a VLA type or the + operand of typeof is a variably modified type or the operand of _Countof has + a variable number of elements or the operand of _Generic is the one selected + as the result. */ static void record_maybe_used_decl (tree decl, bool address) { struct maybe_used_decl *t = XOBNEW (&parser_obstack, struct maybe_used_decl); t->decl = decl; - t->level = in_sizeof + in_typeof + in_countof; + t->level = in_sizeof + in_typeof + in_countof + in_generic; t->address = address; t->next = maybe_used_decls; maybe_used_decls = t; } -/* Pop the stack of decls possibly used inside sizeof or typeof. If - USED is false, just discard them. If it is true, mark them used - (if no longer inside sizeof or typeof) or move them to the next - level up (if still inside sizeof or typeof). */ +/* Pop the stack of decls possibly used inside sizeof or typeof or _Countof or + _Generic. If USED is false, just discard them. If it is true, mark them + used (if no longer inside sizeof or typeof or _Countof or _Generic) or move + them to the next level up (if still inside sizeof or typeof or _Countof or + _Generic). */ void pop_maybe_used (bool used) { struct maybe_used_decl *p = maybe_used_decls; - int cur_level = in_sizeof + in_typeof + in_countof; + int cur_level = in_sizeof + in_typeof + in_countof + in_generic; while (p && p->level > cur_level) { if (used) @@ -3897,6 +3890,35 @@ pop_maybe_used (bool used) maybe_used_decls = p; } +/* Pop the stack of decls possibly used inside sizeof or typeof or _Countof or + _Generic, without acting on them, and return the pointer to the previous top + of the stack. This for use at the end of a default generic association when + it is not yet known whether the expression is used. If it later turns out + the expression is used (or treated as used before C23), restore_maybe_used + should be called on the return value followed by pop_maybe_used (true); + otherwise, the return value can be discarded. */ + +struct maybe_used_decl * +save_maybe_used () +{ + struct maybe_used_decl *p = maybe_used_decls, *orig = p; + int cur_level = in_sizeof + in_typeof + in_countof + in_generic; + while (p && p->level > cur_level) + p = p->next; + maybe_used_decls = p; + return orig; +} + +/* Restore the stack of decls possibly used inside sizeof or typeof or _Countof + or _Generic returned by save_maybe_used. It is required that the stack is + at exactly the point where it was left by save_maybe_used. */ + +void +restore_maybe_used (struct maybe_used_decl *stack) +{ + maybe_used_decls = stack; +} + /* Return the result of sizeof applied to EXPR. */ struct c_expr diff --git a/gcc/testsuite/gcc.dg/c11-generic-4.c b/gcc/testsuite/gcc.dg/c11-generic-4.c new file mode 100644 index 00000000000..41309dff631 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-generic-4.c @@ -0,0 +1,38 @@ +/* Test references to never-defined static functions in _Generic: allowed in + certain places for C23 but not before. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +static int ok1_c23 (); /* { dg-error "used but never defined" } */ +static int ok2_c23 (); /* { dg-error "used but never defined" } */ +static int ok3_c23 (); /* { dg-error "used but never defined" } */ +static int ok4_c23 (); /* { dg-error "used but never defined" } */ +static int ok5_c23 (); /* { dg-error "used but never defined" } */ +static int ok6 (); +static int ok7 (); +static int ok8 (); +static int ok9 (); +static int ok10 (); +static int ok11 (); +static int ok12 (); +static int not_ok1 (); /* { dg-error "used but never defined" } */ +static int not_ok2 (); /* { dg-error "used but never defined" } */ + +void +f () +{ + _Generic (ok1_c23 (), int: 2); + _Generic (1, int: 2, default: ok2_c23 ()); + _Generic (1, default: ok3_c23 (), int: 3); + _Generic (1, int: 2, float: ok4_c23 ()); + _Generic (1, float: ok5_c23 (), int: 3); + sizeof (_Generic (ok8 (), int: 2)); + sizeof (_Generic (1, int: 2, default: ok9 ())); + sizeof (_Generic (1, default: ok10 (), int: 3)); + sizeof (_Generic (1, int: 2, float: ok11 ())); + sizeof (_Generic (1, float: ok12 (), int: 3)); + _Generic (1.0, int: 2, default: not_ok1 ()); + _Generic (1.0, default: not_ok2 (), int: 3); + sizeof (_Generic (1.0, int: 2, default: ok6 ())); + sizeof (_Generic (1.0, default: ok7 (), int: 3)); +} diff --git a/gcc/testsuite/gcc.dg/c23-generic-5.c b/gcc/testsuite/gcc.dg/c23-generic-5.c new file mode 100644 index 00000000000..4603ec829a2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c23-generic-5.c @@ -0,0 +1,38 @@ +/* Test references to never-defined static functions in _Generic: allowed in + certain places for C23 but not before. */ +/* { dg-do compile } */ +/* { dg-options "-std=c23 -pedantic-errors" } */ + +static int ok1_c23 (); +static int ok2_c23 (); +static int ok3_c23 (); +static int ok4_c23 (); +static int ok5_c23 (); +static int ok6 (); +static int ok7 (); +static int ok8 (); +static int ok9 (); +static int ok10 (); +static int ok11 (); +static int ok12 (); +static int not_ok1 (); /* { dg-error "used but never defined" } */ +static int not_ok2 (); /* { dg-error "used but never defined" } */ + +void +f () +{ + _Generic (ok1_c23 (), int: 2); + _Generic (1, int: 2, default: ok2_c23 ()); + _Generic (1, default: ok3_c23 (), int: 3); + _Generic (1, int: 2, float: ok4_c23 ()); + _Generic (1, float: ok5_c23 (), int: 3); + sizeof (_Generic (ok8 (), int: 2)); + sizeof (_Generic (1, int: 2, default: ok9 ())); + sizeof (_Generic (1, default: ok10 (), int: 3)); + sizeof (_Generic (1, int: 2, float: ok11 ())); + sizeof (_Generic (1, float: ok12 (), int: 3)); + _Generic (1.0, int: 2, default: not_ok1 ()); + _Generic (1.0, default: not_ok2 (), int: 3); + sizeof (_Generic (1.0, int: 2, default: ok6 ())); + sizeof (_Generic (1.0, default: ok7 (), int: 3)); +} diff --git a/gcc/testsuite/gcc.dg/c2y-generic-5.c b/gcc/testsuite/gcc.dg/c2y-generic-5.c new file mode 100644 index 00000000000..80097f0ea37 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2y-generic-5.c @@ -0,0 +1,13 @@ +/* Test references to never-defined static functions in _Generic: still not + allowed in a type name used for selection, only an expression (may change if + "discarded" is adopted). */ +/* { dg-do compile } */ +/* { dg-options "-std=c2y -pedantic-errors" } */ + +static int not_ok1 (); /* { dg-error "used but never defined" } */ + +void +f () +{ + _Generic (int (*)[not_ok1 ()], default: 1); +}