From 433cc7b037fbadc15e43083aed48e2f71e0e282f Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Tue, 23 Jul 2013 01:54:24 +0000 Subject: [PATCH] c-common.h (enum rid): New constant. 2013-07-23 Tom Tromey Joseph Myers c-family: * c-common.h (enum rid) : New constant. * c-common.c (c_common_reswords): Add _Generic. c: * c-parser.c (struct c_generic_association): New. (c_generic_association_d): New typedef. (c_parser_generic_selection): New function. (c_parser_postfix_expression): Handle RID_GENERIC. testsuite: * gcc.dg/c11-generic-1.c: New file. * gcc.dg/c11-generic-2.c: New file. Co-Authored-By: Joseph Myers From-SVN: r201153 --- gcc/c-family/ChangeLog | 5 + gcc/c-family/c-common.c | 1 + gcc/c-family/c-common.h | 2 +- gcc/c/ChangeLog | 8 + gcc/c/c-parser.c | 223 +++++++++++++++++++++++++++ gcc/testsuite/ChangeLog | 6 + gcc/testsuite/gcc.dg/c11-generic-1.c | 57 +++++++ gcc/testsuite/gcc.dg/c11-generic-2.c | 27 ++++ 8 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/c11-generic-1.c create mode 100644 gcc/testsuite/gcc.dg/c11-generic-2.c diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 66952066dae..487f880e432 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,8 @@ +2013-07-23 Tom Tromey + + * c-common.h (enum rid) : New constant. + * c-common.c (c_common_reswords): Add _Generic. + 2013-07-21 Ondřej Bílka * c-common.c: Fix typos. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 04d1bd50fa0..7bba376f369 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -412,6 +412,7 @@ const struct c_common_resword c_common_reswords[] = { "_Sat", RID_SAT, D_CONLY | D_EXT }, { "_Static_assert", RID_STATIC_ASSERT, D_CONLY }, { "_Noreturn", RID_NORETURN, D_CONLY }, + { "_Generic", RID_GENERIC, D_CONLY }, { "__FUNCTION__", RID_FUNCTION_NAME, 0 }, { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 }, { "__alignof", RID_ALIGNOF, 0 }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 915887cf327..dc430c3859c 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -105,7 +105,7 @@ enum rid RID_FRACT, RID_ACCUM, /* C11 */ - RID_ALIGNAS, + RID_ALIGNAS, RID_GENERIC, /* This means to warn that this is a C++ keyword, and then treat it as a normal identifier. */ diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 15bc1aa26fc..c7ecff8bdae 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,11 @@ +2013-07-23 Tom Tromey + Joseph Myers + + * c-parser.c (struct c_generic_association): New. + (c_generic_association_d): New typedef. + (c_parser_generic_selection): New function. + (c_parser_postfix_expression): Handle RID_GENERIC. + 2013-07-13 Jason Merrill PR c++/57793 diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index c7846cedccb..fea153a3808 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -6232,6 +6232,225 @@ c_parser_get_builtin_args (c_parser *parser, const char *bname, return true; } +/* This represents a single generic-association. */ + +struct c_generic_association +{ + /* The location of the starting token of the type. */ + location_t type_location; + /* The association's type, or NULL_TREE for 'default'.. */ + tree type; + /* The association's expression. */ + struct c_expr expression; +}; + +/* Parse a generic-selection. (C11 6.5.1.1). + + generic-selection: + _Generic ( assignment-expression , generic-assoc-list ) + + generic-assoc-list: + generic-association + generic-assoc-list , generic-association + + generic-association: + type-name : assignment-expression + default : assignment-expression +*/ + +static struct c_expr +c_parser_generic_selection (c_parser *parser) +{ + vec associations = vNULL; + struct c_expr selector, error_expr; + tree selector_type; + struct c_generic_association matched_assoc; + bool match_found = false; + location_t generic_loc, selector_loc; + + error_expr.original_code = ERROR_MARK; + error_expr.original_type = NULL; + error_expr.value = error_mark_node; + matched_assoc.type_location = UNKNOWN_LOCATION; + matched_assoc.type = NULL_TREE; + matched_assoc.expression = error_expr; + + gcc_assert (c_parser_next_token_is_keyword (parser, RID_GENERIC)); + generic_loc = c_parser_peek_token (parser)->location; + c_parser_consume_token (parser); + if (!flag_isoc11) + { + if (flag_isoc99) + pedwarn (generic_loc, OPT_Wpedantic, + "ISO C99 does not support %<_Generic%>"); + else + pedwarn (generic_loc, OPT_Wpedantic, + "ISO C90 does not support %<_Generic%>"); + } + + if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) + return error_expr; + + c_inhibit_evaluation_warnings++; + selector_loc = c_parser_peek_token (parser)->location; + selector = c_parser_expr_no_commas (parser, NULL); + selector = default_function_array_conversion (selector_loc, selector); + c_inhibit_evaluation_warnings--; + + if (selector.value == error_mark_node) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); + return selector; + } + selector_type = TREE_TYPE (selector.value); + /* In ISO C terms, rvalues (including the controlling expression of + _Generic) do not have qualified types. */ + if (TREE_CODE (selector_type) != ARRAY_TYPE) + selector_type = TYPE_MAIN_VARIANT (selector_type); + /* In ISO C terms, _Noreturn is not part of the type of expressions + such as &abort, but in GCC it is represented internally as a type + qualifier. */ + if (FUNCTION_POINTER_TYPE_P (selector_type) + && TYPE_QUALS (TREE_TYPE (selector_type)) != TYPE_UNQUALIFIED) + selector_type + = build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (selector_type))); + + if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); + return error_expr; + } + + while (1) + { + struct c_generic_association assoc, *iter; + unsigned int ix; + c_token *token = c_parser_peek_token (parser); + + assoc.type_location = token->location; + if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT) + { + c_parser_consume_token (parser); + assoc.type = NULL_TREE; + } + else + { + struct c_type_name *type_name; + + type_name = c_parser_type_name (parser); + if (type_name == NULL) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); + goto error_exit; + } + assoc.type = groktypename (type_name, NULL, NULL); + if (assoc.type == error_mark_node) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); + goto error_exit; + } + + if (TREE_CODE (assoc.type) == FUNCTION_TYPE) + error_at (assoc.type_location, + "%<_Generic%> association has function type"); + else if (!COMPLETE_TYPE_P (assoc.type)) + error_at (assoc.type_location, + "%<_Generic%> association has incomplete type"); + + if (variably_modified_type_p (assoc.type, NULL_TREE)) + error_at (assoc.type_location, + "%<_Generic%> association has " + "variable length type"); + } + + if (!c_parser_require (parser, CPP_COLON, "expected %<:%>")) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); + goto error_exit; + } + + assoc.expression = c_parser_expr_no_commas (parser, NULL); + if (assoc.expression.value == error_mark_node) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); + goto error_exit; + } + + for (ix = 0; associations.iterate (ix, &iter); ++ix) + { + if (assoc.type == NULL_TREE) + { + if (iter->type == NULL_TREE) + { + error_at (assoc.type_location, + "duplicate % case in %<_Generic%>"); + inform (iter->type_location, "original % is here"); + } + } + else if (iter->type != NULL_TREE) + { + if (comptypes (assoc.type, iter->type)) + { + error_at (assoc.type_location, + "%<_Generic%> specifies two compatible types"); + inform (iter->type_location, "compatible type is here"); + } + } + } + + if (assoc.type == NULL_TREE) + { + if (!match_found) + { + matched_assoc = assoc; + match_found = true; + } + } + else if (comptypes (assoc.type, selector_type)) + { + if (!match_found || matched_assoc.type == NULL_TREE) + { + matched_assoc = assoc; + match_found = true; + } + else + { + error_at (assoc.type_location, + "%<_Generic> selector matches multiple associations"); + inform (matched_assoc.type_location, + "other match is here"); + } + } + + associations.safe_push (assoc); + + if (c_parser_peek_token (parser)->type != CPP_COMMA) + break; + c_parser_consume_token (parser); + } + + associations.release (); + + if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) + { + c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); + return error_expr; + } + + if (!match_found) + { + error_at (selector_loc, "%<_Generic%> selector of type %qT is not " + "compatible with any association", + selector_type); + return error_expr; + } + + return matched_assoc.expression; + + error_exit: + associations.release (); + return error_expr; +} /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2). @@ -6255,6 +6474,7 @@ c_parser_get_builtin_args (c_parser *parser, const char *bname, constant string-literal ( expression ) + generic-selection GNU extensions: @@ -6823,6 +7043,9 @@ c_parser_postfix_expression (c_parser *parser) expr.value = objc_build_encode_expr (type); } break; + case RID_GENERIC: + expr = c_parser_generic_selection (parser); + break; default: c_parser_error (parser, "expected expression"); expr.value = error_mark_node; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 97f52c9f1ac..4cd3d0f62b2 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2013-07-23 Tom Tromey + Joseph Myers + + * gcc.dg/c11-generic-1.c: New file. + * gcc.dg/c11-generic-2.c: New file. + 2013-07-22 Tobias Burnus PR fortran/57906 diff --git a/gcc/testsuite/gcc.dg/c11-generic-1.c b/gcc/testsuite/gcc.dg/c11-generic-1.c new file mode 100644 index 00000000000..60ef1f0eb69 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-generic-1.c @@ -0,0 +1,57 @@ +/* Test C11 _Generic. Valid uses. */ +/* { dg-do run } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +_Noreturn extern void exit (int); +_Noreturn extern void abort (void); + +void +check (int n) +{ + if (n) + abort (); +} + +int +main (void) +{ + int n = 0; + + check (_Generic (n++, int: 0)); + /* _Generic should not evaluate its argument. */ + check (n); + + check (_Generic (n, double: n++, default: 0)); + check (n); + + /* Qualifiers are removed for the purpose of type matching. */ + const int cn = 0; + check (_Generic (cn, int: 0, default: n++)); + check (n); + check (_Generic ((const int) n, int: 0, default: n++)); + check (n); + + /* Arrays decay to pointers. */ + int a[1]; + const int ca[1]; + check (_Generic (a, int *: 0, const int *: n++)); + check (n); + check (_Generic (ca, const int *: 0, int *: n++)); + check (n); + + /* Functions decay to pointers. */ + extern void f (void); + check (_Generic (f, void (*) (void): 0, default: n++)); + check (n); + + /* _Noreturn is not part of the function type. */ + check (_Generic (&abort, void (*) (void): 0, default: n++)); + check (n); + + /* Integer promotions do not occur. */ + short s; + check (_Generic (s, short: 0, int: n++)); + check (n); + + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/c11-generic-2.c b/gcc/testsuite/gcc.dg/c11-generic-2.c new file mode 100644 index 00000000000..90be650af28 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c11-generic-2.c @@ -0,0 +1,27 @@ +/* Test C11 _Generic. Error cases. */ +/* { dg-do compile } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +struct incomplete; + +void +f (int n) +{ + /* Multiple 'default's. */ + _Generic (n, default: 1, default: 2); /* { dg-error "duplicate .*default.* case" } */ + + /* Variably-modified type not ok. */ + _Generic (n, int[n]: 0, default: 1); /* { dg-error "variable length type" } */ + /* Type must be complete. */ + _Generic (n, struct incomplete: 0, default: 1); /* { dg-error "incomplete type" } */ + _Generic (n, void: 0, default: 1); /* { dg-error "incomplete type" } */ + + /* Type must be object type. */ + _Generic (n, void (void): 0, default: 1); /* { dg-error "function type" } */ + + /* Two compatible types in association list. */ + _Generic (&n, int: 5, signed int: 7, default: 23); /* { dg-error "two compatible types" } */ + + /* No matching association. */ + _Generic (n, void *: 5); /* { dg-error "not compatible with any association" } */ +}