| /* Parser for C and Objective-C. |
| Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, |
| 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 |
| Free Software Foundation, Inc. |
| |
| Parser actions based on the old Bison parser; structure somewhat |
| influenced by and fragments based on the C++ parser. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* TODO: |
| |
| Make sure all relevant comments, and all relevant code from all |
| actions, brought over from old parser. Verify exact correspondence |
| of syntax accepted. |
| |
| Add testcases covering every input symbol in every state in old and |
| new parsers. |
| |
| Include full syntax for GNU C, including erroneous cases accepted |
| with error messages, in syntax productions in comments. |
| |
| Make more diagnostics in the front end generally take an explicit |
| location rather than implicitly using input_location. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" /* For rtl.h: needs enum reg_class. */ |
| #include "tree.h" |
| #include "langhooks.h" |
| #include "input.h" |
| #include "cpplib.h" |
| #include "timevar.h" |
| #include "c-family/c-pragma.h" |
| #include "c-tree.h" |
| #include "flags.h" |
| #include "output.h" |
| #include "ggc.h" |
| #include "c-family/c-common.h" |
| #include "c-family/c-objc.h" |
| #include "vec.h" |
| #include "target.h" |
| #include "cgraph.h" |
| #include "plugin.h" |
| |
| |
| /* Initialization routine for this file. */ |
| |
| void |
| c_parse_init (void) |
| { |
| /* The only initialization required is of the reserved word |
| identifiers. */ |
| unsigned int i; |
| tree id; |
| int mask = 0; |
| |
| /* Make sure RID_MAX hasn't grown past the 8 bits used to hold the keyword in |
| the c_token structure. */ |
| gcc_assert (RID_MAX <= 255); |
| |
| mask |= D_CXXONLY; |
| if (!flag_isoc99) |
| mask |= D_C99; |
| if (flag_no_asm) |
| { |
| mask |= D_ASM | D_EXT; |
| if (!flag_isoc99) |
| mask |= D_EXT89; |
| } |
| if (!c_dialect_objc ()) |
| mask |= D_OBJC | D_CXX_OBJC; |
| |
| ridpointers = ggc_alloc_cleared_vec_tree ((int) RID_MAX); |
| for (i = 0; i < num_c_common_reswords; i++) |
| { |
| /* If a keyword is disabled, do not enter it into the table |
| and so create a canonical spelling that isn't a keyword. */ |
| if (c_common_reswords[i].disable & mask) |
| { |
| if (warn_cxx_compat |
| && (c_common_reswords[i].disable & D_CXXWARN)) |
| { |
| id = get_identifier (c_common_reswords[i].word); |
| C_SET_RID_CODE (id, RID_CXX_COMPAT_WARN); |
| C_IS_RESERVED_WORD (id) = 1; |
| } |
| continue; |
| } |
| |
| id = get_identifier (c_common_reswords[i].word); |
| C_SET_RID_CODE (id, c_common_reswords[i].rid); |
| C_IS_RESERVED_WORD (id) = 1; |
| ridpointers [(int) c_common_reswords[i].rid] = id; |
| } |
| } |
| |
| /* The C lexer intermediates between the lexer in cpplib and c-lex.c |
| and the C parser. Unlike the C++ lexer, the parser structure |
| stores the lexer information instead of using a separate structure. |
| Identifiers are separated into ordinary identifiers, type names, |
| keywords and some other Objective-C types of identifiers, and some |
| look-ahead is maintained. |
| |
| ??? It might be a good idea to lex the whole file up front (as for |
| C++). It would then be possible to share more of the C and C++ |
| lexer code, if desired. */ |
| |
| /* The following local token type is used. */ |
| |
| /* A keyword. */ |
| #define CPP_KEYWORD ((enum cpp_ttype) (N_TTYPES + 1)) |
| |
| /* More information about the type of a CPP_NAME token. */ |
| typedef enum c_id_kind { |
| /* An ordinary identifier. */ |
| C_ID_ID, |
| /* An identifier declared as a typedef name. */ |
| C_ID_TYPENAME, |
| /* An identifier declared as an Objective-C class name. */ |
| C_ID_CLASSNAME, |
| /* An address space identifier. */ |
| C_ID_ADDRSPACE, |
| /* Not an identifier. */ |
| C_ID_NONE |
| } c_id_kind; |
| |
| /* A single C token after string literal concatenation and conversion |
| of preprocessing tokens to tokens. */ |
| typedef struct GTY (()) c_token { |
| /* The kind of token. */ |
| ENUM_BITFIELD (cpp_ttype) type : 8; |
| /* If this token is a CPP_NAME, this value indicates whether also |
| declared as some kind of type. Otherwise, it is C_ID_NONE. */ |
| ENUM_BITFIELD (c_id_kind) id_kind : 8; |
| /* If this token is a keyword, this value indicates which keyword. |
| Otherwise, this value is RID_MAX. */ |
| ENUM_BITFIELD (rid) keyword : 8; |
| /* If this token is a CPP_PRAGMA, this indicates the pragma that |
| was seen. Otherwise it is PRAGMA_NONE. */ |
| ENUM_BITFIELD (pragma_kind) pragma_kind : 8; |
| /* The location at which this token was found. */ |
| location_t location; |
| /* The value associated with this token, if any. */ |
| tree value; |
| } c_token; |
| |
| /* A parser structure recording information about the state and |
| context of parsing. Includes lexer information with up to two |
| tokens of look-ahead; more are not needed for C. */ |
| typedef struct GTY(()) c_parser { |
| /* The look-ahead tokens. */ |
| c_token tokens[2]; |
| /* How many look-ahead tokens are available (0, 1 or 2). */ |
| short tokens_avail; |
| /* True if a syntax error is being recovered from; false otherwise. |
| c_parser_error sets this flag. It should clear this flag when |
| enough tokens have been consumed to recover from the error. */ |
| BOOL_BITFIELD error : 1; |
| /* True if we're processing a pragma, and shouldn't automatically |
| consume CPP_PRAGMA_EOL. */ |
| BOOL_BITFIELD in_pragma : 1; |
| /* True if we're parsing the outermost block of an if statement. */ |
| BOOL_BITFIELD in_if_block : 1; |
| /* True if we want to lex an untranslated string. */ |
| BOOL_BITFIELD lex_untranslated_string : 1; |
| |
| /* Objective-C specific parser/lexer information. */ |
| |
| /* True if we are in a context where the Objective-C "PQ" keywords |
| are considered keywords. */ |
| BOOL_BITFIELD objc_pq_context : 1; |
| /* True if we are parsing a (potential) Objective-C foreach |
| statement. This is set to true after we parsed 'for (' and while |
| we wait for 'in' or ';' to decide if it's a standard C for loop or an |
| Objective-C foreach loop. */ |
| BOOL_BITFIELD objc_could_be_foreach_context : 1; |
| /* The following flag is needed to contextualize Objective-C lexical |
| analysis. In some cases (e.g., 'int NSObject;'), it is |
| undesirable to bind an identifier to an Objective-C class, even |
| if a class with that name exists. */ |
| BOOL_BITFIELD objc_need_raw_identifier : 1; |
| /* True if we are in a context where the Objective-C "Property attribute" |
| keywords are valid. */ |
| BOOL_BITFIELD objc_property_attr_context : 1; |
| } c_parser; |
| |
| |
| /* The actual parser and external interface. ??? Does this need to be |
| garbage-collected? */ |
| |
| static GTY (()) c_parser *the_parser; |
| |
| /* Read in and lex a single token, storing it in *TOKEN. */ |
| |
| static void |
| c_lex_one_token (c_parser *parser, c_token *token) |
| { |
| timevar_push (TV_LEX); |
| |
| token->type = c_lex_with_flags (&token->value, &token->location, NULL, |
| (parser->lex_untranslated_string |
| ? C_LEX_STRING_NO_TRANSLATE : 0)); |
| token->id_kind = C_ID_NONE; |
| token->keyword = RID_MAX; |
| token->pragma_kind = PRAGMA_NONE; |
| |
| switch (token->type) |
| { |
| case CPP_NAME: |
| { |
| tree decl; |
| |
| bool objc_force_identifier = parser->objc_need_raw_identifier; |
| if (c_dialect_objc ()) |
| parser->objc_need_raw_identifier = false; |
| |
| if (C_IS_RESERVED_WORD (token->value)) |
| { |
| enum rid rid_code = C_RID_CODE (token->value); |
| |
| if (rid_code == RID_CXX_COMPAT_WARN) |
| { |
| warning_at (token->location, |
| OPT_Wc___compat, |
| "identifier %qE conflicts with C++ keyword", |
| token->value); |
| } |
| else if (rid_code >= RID_FIRST_ADDR_SPACE |
| && rid_code <= RID_LAST_ADDR_SPACE) |
| { |
| token->id_kind = C_ID_ADDRSPACE; |
| token->keyword = rid_code; |
| break; |
| } |
| else if (c_dialect_objc () && OBJC_IS_PQ_KEYWORD (rid_code)) |
| { |
| /* We found an Objective-C "pq" keyword (in, out, |
| inout, bycopy, byref, oneway). They need special |
| care because the interpretation depends on the |
| context. */ |
| if (parser->objc_pq_context) |
| { |
| token->type = CPP_KEYWORD; |
| token->keyword = rid_code; |
| break; |
| } |
| else if (parser->objc_could_be_foreach_context |
| && rid_code == RID_IN) |
| { |
| /* We are in Objective-C, inside a (potential) |
| foreach context (which means after having |
| parsed 'for (', but before having parsed ';'), |
| and we found 'in'. We consider it the keyword |
| which terminates the declaration at the |
| beginning of a foreach-statement. Note that |
| this means you can't use 'in' for anything else |
| in that context; in particular, in Objective-C |
| you can't use 'in' as the name of the running |
| variable in a C for loop. We could potentially |
| try to add code here to disambiguate, but it |
| seems a reasonable limitation. */ |
| token->type = CPP_KEYWORD; |
| token->keyword = rid_code; |
| break; |
| } |
| /* Else, "pq" keywords outside of the "pq" context are |
| not keywords, and we fall through to the code for |
| normal tokens. */ |
| } |
| else if (c_dialect_objc () && OBJC_IS_PATTR_KEYWORD (rid_code)) |
| { |
| /* We found an Objective-C "property attribute" |
| keyword (getter, setter, readonly, etc). These are |
| only valid in the property context. */ |
| if (parser->objc_property_attr_context) |
| { |
| token->type = CPP_KEYWORD; |
| token->keyword = rid_code; |
| break; |
| } |
| /* Else they are not special keywords. |
| */ |
| } |
| else if (c_dialect_objc () |
| && (OBJC_IS_AT_KEYWORD (rid_code) |
| || OBJC_IS_CXX_KEYWORD (rid_code))) |
| { |
| /* We found one of the Objective-C "@" keywords (defs, |
| selector, synchronized, etc) or one of the |
| Objective-C "cxx" keywords (class, private, |
| protected, public, try, catch, throw) without a |
| preceding '@' sign. Do nothing and fall through to |
| the code for normal tokens (in C++ we would still |
| consider the CXX ones keywords, but not in C). */ |
| ; |
| } |
| else |
| { |
| token->type = CPP_KEYWORD; |
| token->keyword = rid_code; |
| break; |
| } |
| } |
| |
| decl = lookup_name (token->value); |
| if (decl) |
| { |
| if (TREE_CODE (decl) == TYPE_DECL) |
| { |
| token->id_kind = C_ID_TYPENAME; |
| break; |
| } |
| } |
| else if (c_dialect_objc ()) |
| { |
| tree objc_interface_decl = objc_is_class_name (token->value); |
| /* Objective-C class names are in the same namespace as |
| variables and typedefs, and hence are shadowed by local |
| declarations. */ |
| if (objc_interface_decl |
| && (global_bindings_p () |
| || (!objc_force_identifier && !decl))) |
| { |
| token->value = objc_interface_decl; |
| token->id_kind = C_ID_CLASSNAME; |
| break; |
| } |
| } |
| token->id_kind = C_ID_ID; |
| } |
| break; |
| case CPP_AT_NAME: |
| /* This only happens in Objective-C; it must be a keyword. */ |
| token->type = CPP_KEYWORD; |
| switch (C_RID_CODE (token->value)) |
| { |
| /* Replace 'class' with '@class', 'private' with '@private', |
| etc. This prevents confusion with the C++ keyword |
| 'class', and makes the tokens consistent with other |
| Objective-C 'AT' keywords. For example '@class' is |
| reported as RID_AT_CLASS which is consistent with |
| '@synchronized', which is reported as |
| RID_AT_SYNCHRONIZED. |
| */ |
| case RID_CLASS: token->keyword = RID_AT_CLASS; break; |
| case RID_PRIVATE: token->keyword = RID_AT_PRIVATE; break; |
| case RID_PROTECTED: token->keyword = RID_AT_PROTECTED; break; |
| case RID_PUBLIC: token->keyword = RID_AT_PUBLIC; break; |
| case RID_THROW: token->keyword = RID_AT_THROW; break; |
| case RID_TRY: token->keyword = RID_AT_TRY; break; |
| case RID_CATCH: token->keyword = RID_AT_CATCH; break; |
| default: token->keyword = C_RID_CODE (token->value); |
| } |
| break; |
| case CPP_COLON: |
| case CPP_COMMA: |
| case CPP_CLOSE_PAREN: |
| case CPP_SEMICOLON: |
| /* These tokens may affect the interpretation of any identifiers |
| following, if doing Objective-C. */ |
| if (c_dialect_objc ()) |
| parser->objc_need_raw_identifier = false; |
| break; |
| case CPP_PRAGMA: |
| /* We smuggled the cpp_token->u.pragma value in an INTEGER_CST. */ |
| token->pragma_kind = (enum pragma_kind) TREE_INT_CST_LOW (token->value); |
| token->value = NULL; |
| break; |
| default: |
| break; |
| } |
| timevar_pop (TV_LEX); |
| } |
| |
| /* Return a pointer to the next token from PARSER, reading it in if |
| necessary. */ |
| |
| static inline c_token * |
| c_parser_peek_token (c_parser *parser) |
| { |
| if (parser->tokens_avail == 0) |
| { |
| c_lex_one_token (parser, &parser->tokens[0]); |
| parser->tokens_avail = 1; |
| } |
| return &parser->tokens[0]; |
| } |
| |
| /* Return true if the next token from PARSER has the indicated |
| TYPE. */ |
| |
| static inline bool |
| c_parser_next_token_is (c_parser *parser, enum cpp_ttype type) |
| { |
| return c_parser_peek_token (parser)->type == type; |
| } |
| |
| /* Return true if the next token from PARSER does not have the |
| indicated TYPE. */ |
| |
| static inline bool |
| c_parser_next_token_is_not (c_parser *parser, enum cpp_ttype type) |
| { |
| return !c_parser_next_token_is (parser, type); |
| } |
| |
| /* Return true if the next token from PARSER is the indicated |
| KEYWORD. */ |
| |
| static inline bool |
| c_parser_next_token_is_keyword (c_parser *parser, enum rid keyword) |
| { |
| return c_parser_peek_token (parser)->keyword == keyword; |
| } |
| |
| /* Return a pointer to the next-but-one token from PARSER, reading it |
| in if necessary. The next token is already read in. */ |
| |
| static c_token * |
| c_parser_peek_2nd_token (c_parser *parser) |
| { |
| if (parser->tokens_avail >= 2) |
| return &parser->tokens[1]; |
| gcc_assert (parser->tokens_avail == 1); |
| gcc_assert (parser->tokens[0].type != CPP_EOF); |
| gcc_assert (parser->tokens[0].type != CPP_PRAGMA_EOL); |
| c_lex_one_token (parser, &parser->tokens[1]); |
| parser->tokens_avail = 2; |
| return &parser->tokens[1]; |
| } |
| |
| /* Return true if TOKEN can start a type name, |
| false otherwise. */ |
| static bool |
| c_token_starts_typename (c_token *token) |
| { |
| switch (token->type) |
| { |
| case CPP_NAME: |
| switch (token->id_kind) |
| { |
| case C_ID_ID: |
| return false; |
| case C_ID_ADDRSPACE: |
| return true; |
| case C_ID_TYPENAME: |
| return true; |
| case C_ID_CLASSNAME: |
| gcc_assert (c_dialect_objc ()); |
| return true; |
| default: |
| gcc_unreachable (); |
| } |
| case CPP_KEYWORD: |
| switch (token->keyword) |
| { |
| case RID_UNSIGNED: |
| case RID_LONG: |
| case RID_INT128: |
| case RID_SHORT: |
| case RID_SIGNED: |
| case RID_COMPLEX: |
| case RID_INT: |
| case RID_CHAR: |
| case RID_FLOAT: |
| case RID_DOUBLE: |
| case RID_VOID: |
| case RID_DFLOAT32: |
| case RID_DFLOAT64: |
| case RID_DFLOAT128: |
| case RID_BOOL: |
| case RID_ENUM: |
| case RID_STRUCT: |
| case RID_UNION: |
| case RID_TYPEOF: |
| case RID_CONST: |
| case RID_VOLATILE: |
| case RID_RESTRICT: |
| case RID_ATTRIBUTE: |
| case RID_FRACT: |
| case RID_ACCUM: |
| case RID_SAT: |
| return true; |
| default: |
| return false; |
| } |
| case CPP_LESS: |
| if (c_dialect_objc ()) |
| return true; |
| return false; |
| default: |
| return false; |
| } |
| } |
| |
| enum c_lookahead_kind { |
| /* Always treat unknown identifiers as typenames. */ |
| cla_prefer_type, |
| |
| /* Could be parsing a nonabstract declarator. Only treat an identifier |
| as a typename if followed by another identifier or a star. */ |
| cla_nonabstract_decl, |
| |
| /* Never treat identifiers as typenames. */ |
| cla_prefer_id |
| }; |
| |
| /* Return true if the next token from PARSER can start a type name, |
| false otherwise. LA specifies how to do lookahead in order to |
| detect unknown type names. If unsure, pick CLA_PREFER_ID. */ |
| |
| static inline bool |
| c_parser_next_tokens_start_typename (c_parser *parser, enum c_lookahead_kind la) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| if (c_token_starts_typename (token)) |
| return true; |
| |
| /* Try a bit harder to detect an unknown typename. */ |
| if (la != cla_prefer_id |
| && token->type == CPP_NAME |
| && token->id_kind == C_ID_ID |
| |
| /* Do not try too hard when we could have "object in array". */ |
| && !parser->objc_could_be_foreach_context |
| |
| && (la == cla_prefer_type |
| || c_parser_peek_2nd_token (parser)->type == CPP_NAME |
| || c_parser_peek_2nd_token (parser)->type == CPP_MULT) |
| |
| /* Only unknown identifiers. */ |
| && !lookup_name (token->value)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Return true if TOKEN is a type qualifier, false otherwise. */ |
| static bool |
| c_token_is_qualifier (c_token *token) |
| { |
| switch (token->type) |
| { |
| case CPP_NAME: |
| switch (token->id_kind) |
| { |
| case C_ID_ADDRSPACE: |
| return true; |
| default: |
| return false; |
| } |
| case CPP_KEYWORD: |
| switch (token->keyword) |
| { |
| case RID_CONST: |
| case RID_VOLATILE: |
| case RID_RESTRICT: |
| case RID_ATTRIBUTE: |
| return true; |
| default: |
| return false; |
| } |
| case CPP_LESS: |
| return false; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Return true if the next token from PARSER is a type qualifier, |
| false otherwise. */ |
| static inline bool |
| c_parser_next_token_is_qualifier (c_parser *parser) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| return c_token_is_qualifier (token); |
| } |
| |
| /* Return true if TOKEN can start declaration specifiers, false |
| otherwise. */ |
| static bool |
| c_token_starts_declspecs (c_token *token) |
| { |
| switch (token->type) |
| { |
| case CPP_NAME: |
| switch (token->id_kind) |
| { |
| case C_ID_ID: |
| return false; |
| case C_ID_ADDRSPACE: |
| return true; |
| case C_ID_TYPENAME: |
| return true; |
| case C_ID_CLASSNAME: |
| gcc_assert (c_dialect_objc ()); |
| return true; |
| default: |
| gcc_unreachable (); |
| } |
| case CPP_KEYWORD: |
| switch (token->keyword) |
| { |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_REGISTER: |
| case RID_TYPEDEF: |
| case RID_INLINE: |
| case RID_AUTO: |
| case RID_THREAD: |
| case RID_UNSIGNED: |
| case RID_LONG: |
| case RID_INT128: |
| case RID_SHORT: |
| case RID_SIGNED: |
| case RID_COMPLEX: |
| case RID_INT: |
| case RID_CHAR: |
| case RID_FLOAT: |
| case RID_DOUBLE: |
| case RID_VOID: |
| case RID_DFLOAT32: |
| case RID_DFLOAT64: |
| case RID_DFLOAT128: |
| case RID_BOOL: |
| case RID_ENUM: |
| case RID_STRUCT: |
| case RID_UNION: |
| case RID_TYPEOF: |
| case RID_CONST: |
| case RID_VOLATILE: |
| case RID_RESTRICT: |
| case RID_ATTRIBUTE: |
| case RID_FRACT: |
| case RID_ACCUM: |
| case RID_SAT: |
| return true; |
| default: |
| return false; |
| } |
| case CPP_LESS: |
| if (c_dialect_objc ()) |
| return true; |
| return false; |
| default: |
| return false; |
| } |
| } |
| |
| |
| /* Return true if TOKEN can start declaration specifiers or a static |
| assertion, false otherwise. */ |
| static bool |
| c_token_starts_declaration (c_token *token) |
| { |
| if (c_token_starts_declspecs (token) |
| || token->keyword == RID_STATIC_ASSERT) |
| return true; |
| else |
| return false; |
| } |
| |
| /* Return true if the next token from PARSER can start declaration |
| specifiers, false otherwise. */ |
| static inline bool |
| c_parser_next_token_starts_declspecs (c_parser *parser) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| |
| /* In Objective-C, a classname normally starts a declspecs unless it |
| is immediately followed by a dot. In that case, it is the |
| Objective-C 2.0 "dot-syntax" for class objects, ie, calls the |
| setter/getter on the class. c_token_starts_declspecs() can't |
| differentiate between the two cases because it only checks the |
| current token, so we have a special check here. */ |
| if (c_dialect_objc () |
| && token->type == CPP_NAME |
| && token->id_kind == C_ID_CLASSNAME |
| && c_parser_peek_2nd_token (parser)->type == CPP_DOT) |
| return false; |
| |
| return c_token_starts_declspecs (token); |
| } |
| |
| /* Return true if the next tokens from PARSER can start declaration |
| specifiers or a static assertion, false otherwise. */ |
| static inline bool |
| c_parser_next_tokens_start_declaration (c_parser *parser) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| |
| /* Same as above. */ |
| if (c_dialect_objc () |
| && token->type == CPP_NAME |
| && token->id_kind == C_ID_CLASSNAME |
| && c_parser_peek_2nd_token (parser)->type == CPP_DOT) |
| return false; |
| |
| /* Labels do not start declarations. */ |
| if (token->type == CPP_NAME |
| && c_parser_peek_2nd_token (parser)->type == CPP_COLON) |
| return false; |
| |
| if (c_token_starts_declaration (token)) |
| return true; |
| |
| if (c_parser_next_tokens_start_typename (parser, cla_nonabstract_decl)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Consume the next token from PARSER. */ |
| |
| static void |
| c_parser_consume_token (c_parser *parser) |
| { |
| gcc_assert (parser->tokens_avail >= 1); |
| gcc_assert (parser->tokens[0].type != CPP_EOF); |
| gcc_assert (!parser->in_pragma || parser->tokens[0].type != CPP_PRAGMA_EOL); |
| gcc_assert (parser->error || parser->tokens[0].type != CPP_PRAGMA); |
| if (parser->tokens_avail == 2) |
| parser->tokens[0] = parser->tokens[1]; |
| parser->tokens_avail--; |
| } |
| |
| /* Expect the current token to be a #pragma. Consume it and remember |
| that we've begun parsing a pragma. */ |
| |
| static void |
| c_parser_consume_pragma (c_parser *parser) |
| { |
| gcc_assert (!parser->in_pragma); |
| gcc_assert (parser->tokens_avail >= 1); |
| gcc_assert (parser->tokens[0].type == CPP_PRAGMA); |
| if (parser->tokens_avail == 2) |
| parser->tokens[0] = parser->tokens[1]; |
| parser->tokens_avail--; |
| parser->in_pragma = true; |
| } |
| |
| /* Update the globals input_location and in_system_header from |
| TOKEN. */ |
| static inline void |
| c_parser_set_source_position_from_token (c_token *token) |
| { |
| if (token->type != CPP_EOF) |
| { |
| input_location = token->location; |
| } |
| } |
| |
| /* Issue a diagnostic of the form |
| FILE:LINE: MESSAGE before TOKEN |
| where TOKEN is the next token in the input stream of PARSER. |
| MESSAGE (specified by the caller) is usually of the form "expected |
| OTHER-TOKEN". |
| |
| Do not issue a diagnostic if still recovering from an error. |
| |
| ??? This is taken from the C++ parser, but building up messages in |
| this way is not i18n-friendly and some other approach should be |
| used. */ |
| |
| static void |
| c_parser_error (c_parser *parser, const char *gmsgid) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| if (parser->error) |
| return; |
| parser->error = true; |
| if (!gmsgid) |
| return; |
| /* This diagnostic makes more sense if it is tagged to the line of |
| the token we just peeked at. */ |
| c_parser_set_source_position_from_token (token); |
| c_parse_error (gmsgid, |
| /* Because c_parse_error does not understand |
| CPP_KEYWORD, keywords are treated like |
| identifiers. */ |
| (token->type == CPP_KEYWORD ? CPP_NAME : token->type), |
| /* ??? The C parser does not save the cpp flags of a |
| token, we need to pass 0 here and we will not get |
| the source spelling of some tokens but rather the |
| canonical spelling. */ |
| token->value, /*flags=*/0); |
| } |
| |
| /* If the next token is of the indicated TYPE, consume it. Otherwise, |
| issue the error MSGID. If MSGID is NULL then a message has already |
| been produced and no message will be produced this time. Returns |
| true if found, false otherwise. */ |
| |
| static bool |
| c_parser_require (c_parser *parser, |
| enum cpp_ttype type, |
| const char *msgid) |
| { |
| if (c_parser_next_token_is (parser, type)) |
| { |
| c_parser_consume_token (parser); |
| return true; |
| } |
| else |
| { |
| c_parser_error (parser, msgid); |
| return false; |
| } |
| } |
| |
| /* If the next token is the indicated keyword, consume it. Otherwise, |
| issue the error MSGID. Returns true if found, false otherwise. */ |
| |
| static bool |
| c_parser_require_keyword (c_parser *parser, |
| enum rid keyword, |
| const char *msgid) |
| { |
| if (c_parser_next_token_is_keyword (parser, keyword)) |
| { |
| c_parser_consume_token (parser); |
| return true; |
| } |
| else |
| { |
| c_parser_error (parser, msgid); |
| return false; |
| } |
| } |
| |
| /* Like c_parser_require, except that tokens will be skipped until the |
| desired token is found. An error message is still produced if the |
| next token is not as expected. If MSGID is NULL then a message has |
| already been produced and no message will be produced this |
| time. */ |
| |
| static void |
| c_parser_skip_until_found (c_parser *parser, |
| enum cpp_ttype type, |
| const char *msgid) |
| { |
| unsigned nesting_depth = 0; |
| |
| if (c_parser_require (parser, type, msgid)) |
| return; |
| |
| /* Skip tokens until the desired token is found. */ |
| while (true) |
| { |
| /* Peek at the next token. */ |
| c_token *token = c_parser_peek_token (parser); |
| /* If we've reached the token we want, consume it and stop. */ |
| if (token->type == type && !nesting_depth) |
| { |
| c_parser_consume_token (parser); |
| break; |
| } |
| |
| /* If we've run out of tokens, stop. */ |
| if (token->type == CPP_EOF) |
| return; |
| if (token->type == CPP_PRAGMA_EOL && parser->in_pragma) |
| return; |
| if (token->type == CPP_OPEN_BRACE |
| || token->type == CPP_OPEN_PAREN |
| || token->type == CPP_OPEN_SQUARE) |
| ++nesting_depth; |
| else if (token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_CLOSE_PAREN |
| || token->type == CPP_CLOSE_SQUARE) |
| { |
| if (nesting_depth-- == 0) |
| break; |
| } |
| /* Consume this token. */ |
| c_parser_consume_token (parser); |
| } |
| parser->error = false; |
| } |
| |
| /* Skip tokens until the end of a parameter is found, but do not |
| consume the comma, semicolon or closing delimiter. */ |
| |
| static void |
| c_parser_skip_to_end_of_parameter (c_parser *parser) |
| { |
| unsigned nesting_depth = 0; |
| |
| while (true) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| if ((token->type == CPP_COMMA || token->type == CPP_SEMICOLON) |
| && !nesting_depth) |
| break; |
| /* If we've run out of tokens, stop. */ |
| if (token->type == CPP_EOF) |
| return; |
| if (token->type == CPP_PRAGMA_EOL && parser->in_pragma) |
| return; |
| if (token->type == CPP_OPEN_BRACE |
| || token->type == CPP_OPEN_PAREN |
| || token->type == CPP_OPEN_SQUARE) |
| ++nesting_depth; |
| else if (token->type == CPP_CLOSE_BRACE |
| || token->type == CPP_CLOSE_PAREN |
| || token->type == CPP_CLOSE_SQUARE) |
| { |
| if (nesting_depth-- == 0) |
| break; |
| } |
| /* Consume this token. */ |
| c_parser_consume_token (parser); |
| } |
| parser->error = false; |
| } |
| |
| /* Expect to be at the end of the pragma directive and consume an |
| end of line marker. */ |
| |
| static void |
| c_parser_skip_to_pragma_eol (c_parser *parser) |
| { |
| gcc_assert (parser->in_pragma); |
| parser->in_pragma = false; |
| |
| if (!c_parser_require (parser, CPP_PRAGMA_EOL, "expected end of line")) |
| while (true) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| if (token->type == CPP_EOF) |
| break; |
| if (token->type == CPP_PRAGMA_EOL) |
| { |
| c_parser_consume_token (parser); |
| break; |
| } |
| c_parser_consume_token (parser); |
| } |
| |
| parser->error = false; |
| } |
| |
| /* Skip tokens until we have consumed an entire block, or until we |
| have consumed a non-nested ';'. */ |
| |
| static void |
| c_parser_skip_to_end_of_block_or_statement (c_parser *parser) |
| { |
| unsigned nesting_depth = 0; |
| bool save_error = parser->error; |
| |
| while (true) |
| { |
| c_token *token; |
| |
| /* Peek at the next token. */ |
| token = c_parser_peek_token (parser); |
| |
| switch (token->type) |
| { |
| case CPP_EOF: |
| return; |
| |
| case CPP_PRAGMA_EOL: |
| if (parser->in_pragma) |
| return; |
| break; |
| |
| case CPP_SEMICOLON: |
| /* If the next token is a ';', we have reached the |
| end of the statement. */ |
| if (!nesting_depth) |
| { |
| /* Consume the ';'. */ |
| c_parser_consume_token (parser); |
| goto finished; |
| } |
| break; |
| |
| case CPP_CLOSE_BRACE: |
| /* If the next token is a non-nested '}', then we have |
| reached the end of the current block. */ |
| if (nesting_depth == 0 || --nesting_depth == 0) |
| { |
| c_parser_consume_token (parser); |
| goto finished; |
| } |
| break; |
| |
| case CPP_OPEN_BRACE: |
| /* If it the next token is a '{', then we are entering a new |
| block. Consume the entire block. */ |
| ++nesting_depth; |
| break; |
| |
| case CPP_PRAGMA: |
| /* If we see a pragma, consume the whole thing at once. We |
| have some safeguards against consuming pragmas willy-nilly. |
| Normally, we'd expect to be here with parser->error set, |
| which disables these safeguards. But it's possible to get |
| here for secondary error recovery, after parser->error has |
| been cleared. */ |
| c_parser_consume_pragma (parser); |
| c_parser_skip_to_pragma_eol (parser); |
| parser->error = save_error; |
| continue; |
| |
| default: |
| break; |
| } |
| |
| c_parser_consume_token (parser); |
| } |
| |
| finished: |
| parser->error = false; |
| } |
| |
| /* CPP's options (initialized by c-opts.c). */ |
| extern cpp_options *cpp_opts; |
| |
| /* Save the warning flags which are controlled by __extension__. */ |
| |
| static inline int |
| disable_extension_diagnostics (void) |
| { |
| int ret = (pedantic |
| | (warn_pointer_arith << 1) |
| | (warn_traditional << 2) |
| | (flag_iso << 3) |
| | (warn_long_long << 4) |
| | (warn_cxx_compat << 5)); |
| cpp_opts->cpp_pedantic = pedantic = 0; |
| warn_pointer_arith = 0; |
| cpp_opts->cpp_warn_traditional = warn_traditional = 0; |
| flag_iso = 0; |
| cpp_opts->cpp_warn_long_long = warn_long_long = 0; |
| warn_cxx_compat = 0; |
| return ret; |
| } |
| |
| /* Restore the warning flags which are controlled by __extension__. |
| FLAGS is the return value from disable_extension_diagnostics. */ |
| |
| static inline void |
| restore_extension_diagnostics (int flags) |
| { |
| cpp_opts->cpp_pedantic = pedantic = flags & 1; |
| warn_pointer_arith = (flags >> 1) & 1; |
| cpp_opts->cpp_warn_traditional = warn_traditional = (flags >> 2) & 1; |
| flag_iso = (flags >> 3) & 1; |
| cpp_opts->cpp_warn_long_long = warn_long_long = (flags >> 4) & 1; |
| warn_cxx_compat = (flags >> 5) & 1; |
| } |
| |
| /* Possibly kinds of declarator to parse. */ |
| typedef enum c_dtr_syn { |
| /* A normal declarator with an identifier. */ |
| C_DTR_NORMAL, |
| /* An abstract declarator (maybe empty). */ |
| C_DTR_ABSTRACT, |
| /* A parameter declarator: may be either, but after a type name does |
| not redeclare a typedef name as an identifier if it can |
| alternatively be interpreted as a typedef name; see DR#009, |
| applied in C90 TC1, omitted from C99 and reapplied in C99 TC2 |
| following DR#249. For example, given a typedef T, "int T" and |
| "int *T" are valid parameter declarations redeclaring T, while |
| "int (T)" and "int * (T)" and "int (T[])" and "int (T (int))" are |
| abstract declarators rather than involving redundant parentheses; |
| the same applies with attributes inside the parentheses before |
| "T". */ |
| C_DTR_PARM |
| } c_dtr_syn; |
| |
| static void c_parser_external_declaration (c_parser *); |
| static void c_parser_asm_definition (c_parser *); |
| static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, |
| bool, bool, tree *); |
| static void c_parser_static_assert_declaration_no_semi (c_parser *); |
| static void c_parser_static_assert_declaration (c_parser *); |
| static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool, |
| bool, enum c_lookahead_kind); |
| static struct c_typespec c_parser_enum_specifier (c_parser *); |
| static struct c_typespec c_parser_struct_or_union_specifier (c_parser *); |
| static tree c_parser_struct_declaration (c_parser *); |
| static struct c_typespec c_parser_typeof_specifier (c_parser *); |
| static struct c_declarator *c_parser_declarator (c_parser *, bool, c_dtr_syn, |
| bool *); |
| static struct c_declarator *c_parser_direct_declarator (c_parser *, bool, |
| c_dtr_syn, bool *); |
| static struct c_declarator *c_parser_direct_declarator_inner (c_parser *, |
| bool, |
| struct c_declarator *); |
| static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree); |
| static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree); |
| static struct c_parm *c_parser_parameter_declaration (c_parser *, tree); |
| static tree c_parser_simple_asm_expr (c_parser *); |
| static tree c_parser_attributes (c_parser *); |
| static struct c_type_name *c_parser_type_name (c_parser *); |
| static struct c_expr c_parser_initializer (c_parser *); |
| static struct c_expr c_parser_braced_init (c_parser *, tree, bool); |
| static void c_parser_initelt (c_parser *, struct obstack *); |
| static void c_parser_initval (c_parser *, struct c_expr *, |
| struct obstack *); |
| static tree c_parser_compound_statement (c_parser *); |
| static void c_parser_compound_statement_nostart (c_parser *); |
| static void c_parser_label (c_parser *); |
| static void c_parser_statement (c_parser *); |
| static void c_parser_statement_after_labels (c_parser *); |
| static void c_parser_if_statement (c_parser *); |
| static void c_parser_switch_statement (c_parser *); |
| static void c_parser_while_statement (c_parser *); |
| static void c_parser_do_statement (c_parser *); |
| static void c_parser_for_statement (c_parser *); |
| static tree c_parser_asm_statement (c_parser *); |
| static tree c_parser_asm_operands (c_parser *, bool); |
| static tree c_parser_asm_goto_operands (c_parser *); |
| static tree c_parser_asm_clobbers (c_parser *); |
| static struct c_expr c_parser_expr_no_commas (c_parser *, struct c_expr *); |
| static struct c_expr c_parser_conditional_expression (c_parser *, |
| struct c_expr *); |
| static struct c_expr c_parser_binary_expression (c_parser *, struct c_expr *); |
| static struct c_expr c_parser_cast_expression (c_parser *, struct c_expr *); |
| static struct c_expr c_parser_unary_expression (c_parser *); |
| static struct c_expr c_parser_sizeof_expression (c_parser *); |
| static struct c_expr c_parser_alignof_expression (c_parser *); |
| static struct c_expr c_parser_postfix_expression (c_parser *); |
| static struct c_expr c_parser_postfix_expression_after_paren_type (c_parser *, |
| struct c_type_name *, |
| location_t); |
| static struct c_expr c_parser_postfix_expression_after_primary (c_parser *, |
| location_t loc, |
| struct c_expr); |
| static struct c_expr c_parser_expression (c_parser *); |
| static struct c_expr c_parser_expression_conv (c_parser *); |
| static VEC(tree,gc) *c_parser_expr_list (c_parser *, bool, bool, |
| VEC(tree,gc) **); |
| static void c_parser_omp_construct (c_parser *); |
| static void c_parser_omp_threadprivate (c_parser *); |
| static void c_parser_omp_barrier (c_parser *); |
| static void c_parser_omp_flush (c_parser *); |
| static void c_parser_omp_taskwait (c_parser *); |
| |
| enum pragma_context { pragma_external, pragma_stmt, pragma_compound }; |
| static bool c_parser_pragma (c_parser *, enum pragma_context); |
| |
| /* These Objective-C parser functions are only ever called when |
| compiling Objective-C. */ |
| static void c_parser_objc_class_definition (c_parser *, tree); |
| static void c_parser_objc_class_instance_variables (c_parser *); |
| static void c_parser_objc_class_declaration (c_parser *); |
| static void c_parser_objc_alias_declaration (c_parser *); |
| static void c_parser_objc_protocol_definition (c_parser *, tree); |
| static bool c_parser_objc_method_type (c_parser *); |
| static void c_parser_objc_method_definition (c_parser *); |
| static void c_parser_objc_methodprotolist (c_parser *); |
| static void c_parser_objc_methodproto (c_parser *); |
| static tree c_parser_objc_method_decl (c_parser *, bool, tree *); |
| static tree c_parser_objc_type_name (c_parser *); |
| static tree c_parser_objc_protocol_refs (c_parser *); |
| static void c_parser_objc_try_catch_finally_statement (c_parser *); |
| static void c_parser_objc_synchronized_statement (c_parser *); |
| static tree c_parser_objc_selector (c_parser *); |
| static tree c_parser_objc_selector_arg (c_parser *); |
| static tree c_parser_objc_receiver (c_parser *); |
| static tree c_parser_objc_message_args (c_parser *); |
| static tree c_parser_objc_keywordexpr (c_parser *); |
| static void c_parser_objc_at_property_declaration (c_parser *); |
| static void c_parser_objc_at_synthesize_declaration (c_parser *); |
| static void c_parser_objc_at_dynamic_declaration (c_parser *); |
| static bool c_parser_objc_diagnose_bad_element_prefix |
| (c_parser *, struct c_declspecs *); |
| |
| /* Parse a translation unit (C90 6.7, C99 6.9). |
| |
| translation-unit: |
| external-declarations |
| |
| external-declarations: |
| external-declaration |
| external-declarations external-declaration |
| |
| GNU extensions: |
| |
| translation-unit: |
| empty |
| */ |
| |
| static void |
| c_parser_translation_unit (c_parser *parser) |
| { |
| if (c_parser_next_token_is (parser, CPP_EOF)) |
| { |
| pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, |
| "ISO C forbids an empty translation unit"); |
| } |
| else |
| { |
| void *obstack_position = obstack_alloc (&parser_obstack, 0); |
| mark_valid_location_for_stdc_pragma (false); |
| do |
| { |
| ggc_collect (); |
| c_parser_external_declaration (parser); |
| obstack_free (&parser_obstack, obstack_position); |
| } |
| while (c_parser_next_token_is_not (parser, CPP_EOF)); |
| } |
| } |
| |
| /* Parse an external declaration (C90 6.7, C99 6.9). |
| |
| external-declaration: |
| function-definition |
| declaration |
| |
| GNU extensions: |
| |
| external-declaration: |
| asm-definition |
| ; |
| __extension__ external-declaration |
| |
| Objective-C: |
| |
| external-declaration: |
| objc-class-definition |
| objc-class-declaration |
| objc-alias-declaration |
| objc-protocol-definition |
| objc-method-definition |
| @end |
| */ |
| |
| static void |
| c_parser_external_declaration (c_parser *parser) |
| { |
| int ext; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_KEYWORD: |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_EXTENSION: |
| ext = disable_extension_diagnostics (); |
| c_parser_consume_token (parser); |
| c_parser_external_declaration (parser); |
| restore_extension_diagnostics (ext); |
| break; |
| case RID_ASM: |
| c_parser_asm_definition (parser); |
| break; |
| case RID_AT_INTERFACE: |
| case RID_AT_IMPLEMENTATION: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_class_definition (parser, NULL_TREE); |
| break; |
| case RID_AT_CLASS: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_class_declaration (parser); |
| break; |
| case RID_AT_ALIAS: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_alias_declaration (parser); |
| break; |
| case RID_AT_PROTOCOL: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_protocol_definition (parser, NULL_TREE); |
| break; |
| case RID_AT_PROPERTY: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_at_property_declaration (parser); |
| break; |
| case RID_AT_SYNTHESIZE: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_at_synthesize_declaration (parser); |
| break; |
| case RID_AT_DYNAMIC: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_at_dynamic_declaration (parser); |
| break; |
| case RID_AT_END: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_consume_token (parser); |
| objc_finish_implementation (); |
| break; |
| default: |
| goto decl_or_fndef; |
| } |
| break; |
| case CPP_SEMICOLON: |
| pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, |
| "ISO C does not allow extra %<;%> outside of a function"); |
| c_parser_consume_token (parser); |
| break; |
| case CPP_PRAGMA: |
| mark_valid_location_for_stdc_pragma (true); |
| c_parser_pragma (parser, pragma_external); |
| mark_valid_location_for_stdc_pragma (false); |
| break; |
| case CPP_PLUS: |
| case CPP_MINUS: |
| if (c_dialect_objc ()) |
| { |
| c_parser_objc_method_definition (parser); |
| break; |
| } |
| /* Else fall through, and yield a syntax error trying to parse |
| as a declaration or function definition. */ |
| default: |
| decl_or_fndef: |
| /* A declaration or a function definition (or, in Objective-C, |
| an @interface or @protocol with prefix attributes). We can |
| only tell which after parsing the declaration specifiers, if |
| any, and the first declarator. */ |
| c_parser_declaration_or_fndef (parser, true, true, true, false, true, NULL); |
| break; |
| } |
| } |
| |
| /* Parse a declaration or function definition (C90 6.5, 6.7.1, C99 |
| 6.7, 6.9.1). If FNDEF_OK is true, a function definition is |
| accepted; otherwise (old-style parameter declarations) only other |
| declarations are accepted. If STATIC_ASSERT_OK is true, a static |
| assertion is accepted; otherwise (old-style parameter declarations) |
| it is not. If NESTED is true, we are inside a function or parsing |
| old-style parameter declarations; any functions encountered are |
| nested functions and declaration specifiers are required; otherwise |
| we are at top level and functions are normal functions and |
| declaration specifiers may be optional. If EMPTY_OK is true, empty |
| declarations are OK (subject to all other constraints); otherwise |
| (old-style parameter declarations) they are diagnosed. If |
| START_ATTR_OK is true, the declaration specifiers may start with |
| attributes; otherwise they may not. |
| OBJC_FOREACH_OBJECT_DECLARATION can be used to get back the parsed |
| declaration when parsing an Objective-C foreach statement. |
| |
| declaration: |
| declaration-specifiers init-declarator-list[opt] ; |
| static_assert-declaration |
| |
| function-definition: |
| declaration-specifiers[opt] declarator declaration-list[opt] |
| compound-statement |
| |
| declaration-list: |
| declaration |
| declaration-list declaration |
| |
| init-declarator-list: |
| init-declarator |
| init-declarator-list , init-declarator |
| |
| init-declarator: |
| declarator simple-asm-expr[opt] attributes[opt] |
| declarator simple-asm-expr[opt] attributes[opt] = initializer |
| |
| GNU extensions: |
| |
| nested-function-definition: |
| declaration-specifiers declarator declaration-list[opt] |
| compound-statement |
| |
| Objective-C: |
| attributes objc-class-definition |
| attributes objc-category-definition |
| attributes objc-protocol-definition |
| |
| The simple-asm-expr and attributes are GNU extensions. |
| |
| This function does not handle __extension__; that is handled in its |
| callers. ??? Following the old parser, __extension__ may start |
| external declarations, declarations in functions and declarations |
| at the start of "for" loops, but not old-style parameter |
| declarations. |
| |
| C99 requires declaration specifiers in a function definition; the |
| absence is diagnosed through the diagnosis of implicit int. In GNU |
| C we also allow but diagnose declarations without declaration |
| specifiers, but only at top level (elsewhere they conflict with |
| other syntax). |
| |
| In Objective-C, declarations of the looping variable in a foreach |
| statement are exceptionally terminated by 'in' (for example, 'for |
| (NSObject *object in array) { ... }'). |
| |
| OpenMP: |
| |
| declaration: |
| threadprivate-directive */ |
| |
| static void |
| c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, |
| bool static_assert_ok, bool empty_ok, |
| bool nested, bool start_attr_ok, |
| tree *objc_foreach_object_declaration) |
| { |
| struct c_declspecs *specs; |
| tree prefix_attrs; |
| tree all_prefix_attrs; |
| bool diagnosed_no_specs = false; |
| location_t here = c_parser_peek_token (parser)->location; |
| |
| if (static_assert_ok |
| && c_parser_next_token_is_keyword (parser, RID_STATIC_ASSERT)) |
| { |
| c_parser_static_assert_declaration (parser); |
| return; |
| } |
| specs = build_null_declspecs (); |
| |
| /* Try to detect an unknown type name when we have "A B" or "A *B". */ |
| if (c_parser_peek_token (parser)->type == CPP_NAME |
| && c_parser_peek_token (parser)->id_kind == C_ID_ID |
| && (c_parser_peek_2nd_token (parser)->type == CPP_NAME |
| || c_parser_peek_2nd_token (parser)->type == CPP_MULT) |
| && (!nested || !lookup_name (c_parser_peek_token (parser)->value))) |
| { |
| error_at (here, "unknown type name %qE", |
| c_parser_peek_token (parser)->value); |
| |
| /* Parse declspecs normally to get a correct pointer type, but avoid |
| a further "fails to be a type name" error. Refuse nested functions |
| since it is not how the user likely wants us to recover. */ |
| c_parser_peek_token (parser)->type = CPP_KEYWORD; |
| c_parser_peek_token (parser)->keyword = RID_VOID; |
| c_parser_peek_token (parser)->value = error_mark_node; |
| fndef_ok = !nested; |
| } |
| |
| c_parser_declspecs (parser, specs, true, true, start_attr_ok, cla_nonabstract_decl); |
| if (parser->error) |
| { |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| } |
| if (nested && !specs->declspecs_seen_p) |
| { |
| c_parser_error (parser, "expected declaration specifiers"); |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| } |
| finish_declspecs (specs); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| if (empty_ok) |
| shadow_tag (specs); |
| else |
| { |
| shadow_tag_warned (specs, 1); |
| pedwarn (here, 0, "empty declaration"); |
| } |
| c_parser_consume_token (parser); |
| return; |
| } |
| |
| /* Provide better error recovery. Note that a type name here is usually |
| better diagnosed as a redeclaration. */ |
| if (empty_ok |
| && specs->typespec_kind == ctsk_tagdef |
| && c_parser_next_token_starts_declspecs (parser) |
| && !c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected %<;%>, identifier or %<(%>"); |
| parser->error = false; |
| shadow_tag_warned (specs, 1); |
| return; |
| } |
| else if (c_dialect_objc ()) |
| { |
| /* Prefix attributes are an error on method decls. */ |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_PLUS: |
| case CPP_MINUS: |
| if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) |
| return; |
| if (specs->attrs) |
| { |
| warning_at (c_parser_peek_token (parser)->location, |
| OPT_Wattributes, |
| "prefix attributes are ignored for methods"); |
| specs->attrs = NULL_TREE; |
| } |
| if (fndef_ok) |
| c_parser_objc_method_definition (parser); |
| else |
| c_parser_objc_methodproto (parser); |
| return; |
| break; |
| default: |
| break; |
| } |
| /* This is where we parse 'attributes @interface ...', |
| 'attributes @implementation ...', 'attributes @protocol ...' |
| (where attributes could be, for example, __attribute__ |
| ((deprecated)). |
| */ |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_AT_INTERFACE: |
| { |
| if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) |
| return; |
| c_parser_objc_class_definition (parser, specs->attrs); |
| return; |
| } |
| break; |
| case RID_AT_IMPLEMENTATION: |
| { |
| if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) |
| return; |
| if (specs->attrs) |
| { |
| warning_at (c_parser_peek_token (parser)->location, |
| OPT_Wattributes, |
| "prefix attributes are ignored for implementations"); |
| specs->attrs = NULL_TREE; |
| } |
| c_parser_objc_class_definition (parser, NULL_TREE); |
| return; |
| } |
| break; |
| case RID_AT_PROTOCOL: |
| { |
| if (c_parser_objc_diagnose_bad_element_prefix (parser, specs)) |
| return; |
| c_parser_objc_protocol_definition (parser, specs->attrs); |
| return; |
| } |
| break; |
| case RID_AT_ALIAS: |
| case RID_AT_CLASS: |
| case RID_AT_END: |
| case RID_AT_PROPERTY: |
| if (specs->attrs) |
| { |
| c_parser_error (parser, "unexpected attribute"); |
| specs->attrs = NULL; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| pending_xref_error (); |
| prefix_attrs = specs->attrs; |
| all_prefix_attrs = prefix_attrs; |
| specs->attrs = NULL_TREE; |
| while (true) |
| { |
| struct c_declarator *declarator; |
| bool dummy = false; |
| tree fnbody; |
| /* Declaring either one or more declarators (in which case we |
| should diagnose if there were no declaration specifiers) or a |
| function definition (in which case the diagnostic for |
| implicit int suffices). */ |
| declarator = c_parser_declarator (parser, |
| specs->typespec_kind != ctsk_none, |
| C_DTR_NORMAL, &dummy); |
| if (declarator == NULL) |
| { |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| } |
| if (c_parser_next_token_is (parser, CPP_EQ) |
| || c_parser_next_token_is (parser, CPP_COMMA) |
| || c_parser_next_token_is (parser, CPP_SEMICOLON) |
| || c_parser_next_token_is_keyword (parser, RID_ASM) |
| || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE) |
| || c_parser_next_token_is_keyword (parser, RID_IN)) |
| { |
| tree asm_name = NULL_TREE; |
| tree postfix_attrs = NULL_TREE; |
| if (!diagnosed_no_specs && !specs->declspecs_seen_p) |
| { |
| diagnosed_no_specs = true; |
| pedwarn (here, 0, "data definition has no type or storage class"); |
| } |
| /* Having seen a data definition, there cannot now be a |
| function definition. */ |
| fndef_ok = false; |
| if (c_parser_next_token_is_keyword (parser, RID_ASM)) |
| asm_name = c_parser_simple_asm_expr (parser); |
| if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| postfix_attrs = c_parser_attributes (parser); |
| if (c_parser_next_token_is (parser, CPP_EQ)) |
| { |
| tree d; |
| struct c_expr init; |
| location_t init_loc; |
| c_parser_consume_token (parser); |
| /* The declaration of the variable is in effect while |
| its initializer is parsed. */ |
| d = start_decl (declarator, specs, true, |
| chainon (postfix_attrs, all_prefix_attrs)); |
| if (!d) |
| d = error_mark_node; |
| start_init (d, asm_name, global_bindings_p ()); |
| init_loc = c_parser_peek_token (parser)->location; |
| init = c_parser_initializer (parser); |
| finish_init (); |
| if (d != error_mark_node) |
| { |
| maybe_warn_string_init (TREE_TYPE (d), init); |
| finish_decl (d, init_loc, init.value, |
| init.original_type, asm_name); |
| } |
| } |
| else |
| { |
| tree d = start_decl (declarator, specs, false, |
| chainon (postfix_attrs, |
| all_prefix_attrs)); |
| if (d) |
| finish_decl (d, UNKNOWN_LOCATION, NULL_TREE, |
| NULL_TREE, asm_name); |
| |
| if (c_parser_next_token_is_keyword (parser, RID_IN)) |
| { |
| if (d) |
| *objc_foreach_object_declaration = d; |
| else |
| *objc_foreach_object_declaration = error_mark_node; |
| } |
| } |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| all_prefix_attrs = chainon (c_parser_attributes (parser), |
| prefix_attrs); |
| else |
| all_prefix_attrs = prefix_attrs; |
| continue; |
| } |
| else if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| c_parser_consume_token (parser); |
| return; |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_IN)) |
| { |
| /* This can only happen in Objective-C: we found the |
| 'in' that terminates the declaration inside an |
| Objective-C foreach statement. Do not consume the |
| token, so that the caller can use it to determine |
| that this indeed is a foreach context. */ |
| return; |
| } |
| else |
| { |
| c_parser_error (parser, "expected %<,%> or %<;%>"); |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| } |
| } |
| else if (!fndef_ok) |
| { |
| c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, " |
| "%<asm%> or %<__attribute__%>"); |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| } |
| /* Function definition (nested or otherwise). */ |
| if (nested) |
| { |
| pedwarn (here, OPT_pedantic, "ISO C forbids nested functions"); |
| c_push_function_context (); |
| } |
| if (!start_function (specs, declarator, all_prefix_attrs)) |
| { |
| /* This can appear in many cases looking nothing like a |
| function definition, so we don't give a more specific |
| error suggesting there was one. */ |
| c_parser_error (parser, "expected %<=%>, %<,%>, %<;%>, %<asm%> " |
| "or %<__attribute__%>"); |
| if (nested) |
| c_pop_function_context (); |
| break; |
| } |
| /* Parse old-style parameter declarations. ??? Attributes are |
| not allowed to start declaration specifiers here because of a |
| syntax conflict between a function declaration with attribute |
| suffix and a function definition with an attribute prefix on |
| first old-style parameter declaration. Following the old |
| parser, they are not accepted on subsequent old-style |
| parameter declarations either. However, there is no |
| ambiguity after the first declaration, nor indeed on the |
| first as long as we don't allow postfix attributes after a |
| declarator with a nonempty identifier list in a definition; |
| and postfix attributes have never been accepted here in |
| function definitions either. */ |
| while (c_parser_next_token_is_not (parser, CPP_EOF) |
| && c_parser_next_token_is_not (parser, CPP_OPEN_BRACE)) |
| c_parser_declaration_or_fndef (parser, false, false, false, |
| true, false, NULL); |
| store_parm_decls (); |
| DECL_STRUCT_FUNCTION (current_function_decl)->function_start_locus |
| = c_parser_peek_token (parser)->location; |
| fnbody = c_parser_compound_statement (parser); |
| if (nested) |
| { |
| tree decl = current_function_decl; |
| /* Mark nested functions as needing static-chain initially. |
| lower_nested_functions will recompute it but the |
| DECL_STATIC_CHAIN flag is also used before that happens, |
| by initializer_constant_valid_p. See gcc.dg/nested-fn-2.c. */ |
| DECL_STATIC_CHAIN (decl) = 1; |
| add_stmt (fnbody); |
| finish_function (); |
| c_pop_function_context (); |
| add_stmt (build_stmt (DECL_SOURCE_LOCATION (decl), DECL_EXPR, decl)); |
| } |
| else |
| { |
| add_stmt (fnbody); |
| finish_function (); |
| } |
| break; |
| } |
| } |
| |
| /* Parse an asm-definition (asm() outside a function body). This is a |
| GNU extension. |
| |
| asm-definition: |
| simple-asm-expr ; |
| */ |
| |
| static void |
| c_parser_asm_definition (c_parser *parser) |
| { |
| tree asm_str = c_parser_simple_asm_expr (parser); |
| if (asm_str) |
| cgraph_add_asm_node (asm_str); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| |
| /* Parse a static assertion (C1X N1425 6.7.10). |
| |
| static_assert-declaration: |
| static_assert-declaration-no-semi ; |
| */ |
| |
| static void |
| c_parser_static_assert_declaration (c_parser *parser) |
| { |
| c_parser_static_assert_declaration_no_semi (parser); |
| if (parser->error |
| || !c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| } |
| |
| /* Parse a static assertion (C1X N1425 6.7.10), without the trailing |
| semicolon. |
| |
| static_assert-declaration-no-semi: |
| _Static_assert ( constant-expression , string-literal ) |
| */ |
| |
| static void |
| c_parser_static_assert_declaration_no_semi (c_parser *parser) |
| { |
| location_t assert_loc, value_loc; |
| tree value; |
| tree string; |
| |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_STATIC_ASSERT)); |
| assert_loc = c_parser_peek_token (parser)->location; |
| if (!flag_isoc1x) |
| { |
| if (flag_isoc99) |
| pedwarn (assert_loc, OPT_pedantic, |
| "ISO C99 does not support %<_Static_assert%>"); |
| else |
| pedwarn (assert_loc, OPT_pedantic, |
| "ISO C90 does not support %<_Static_assert%>"); |
| } |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return; |
| value_loc = c_parser_peek_token (parser)->location; |
| value = c_parser_expr_no_commas (parser, NULL).value; |
| parser->lex_untranslated_string = true; |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| { |
| parser->lex_untranslated_string = false; |
| return; |
| } |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_STRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_WSTRING: |
| case CPP_UTF8STRING: |
| string = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| parser->lex_untranslated_string = false; |
| break; |
| default: |
| c_parser_error (parser, "expected string literal"); |
| parser->lex_untranslated_string = false; |
| return; |
| } |
| c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| |
| if (!INTEGRAL_TYPE_P (TREE_TYPE (value))) |
| { |
| error_at (value_loc, "expression in static assertion is not an integer"); |
| return; |
| } |
| if (TREE_CODE (value) != INTEGER_CST) |
| { |
| value = c_fully_fold (value, false, NULL); |
| if (TREE_CODE (value) == INTEGER_CST) |
| pedwarn (value_loc, OPT_pedantic, "expression in static assertion " |
| "is not an integer constant expression"); |
| } |
| if (TREE_CODE (value) != INTEGER_CST) |
| { |
| error_at (value_loc, "expression in static assertion is not constant"); |
| return; |
| } |
| constant_expression_warning (value); |
| if (integer_zerop (value)) |
| error_at (assert_loc, "static assertion failed: %E", string); |
| } |
| |
| /* Parse some declaration specifiers (possibly none) (C90 6.5, C99 |
| 6.7), adding them to SPECS (which may already include some). |
| Storage class specifiers are accepted iff SCSPEC_OK; type |
| specifiers are accepted iff TYPESPEC_OK; attributes are accepted at |
| the start iff START_ATTR_OK. |
| |
| declaration-specifiers: |
| storage-class-specifier declaration-specifiers[opt] |
| type-specifier declaration-specifiers[opt] |
| type-qualifier declaration-specifiers[opt] |
| function-specifier declaration-specifiers[opt] |
| |
| Function specifiers (inline) are from C99, and are currently |
| handled as storage class specifiers, as is __thread. |
| |
| C90 6.5.1, C99 6.7.1: |
| storage-class-specifier: |
| typedef |
| extern |
| static |
| auto |
| register |
| |
| C99 6.7.4: |
| function-specifier: |
| inline |
| |
| C90 6.5.2, C99 6.7.2: |
| type-specifier: |
| void |
| char |
| short |
| int |
| long |
| float |
| double |
| signed |
| unsigned |
| _Bool |
| _Complex |
| [_Imaginary removed in C99 TC2] |
| struct-or-union-specifier |
| enum-specifier |
| typedef-name |
| |
| (_Bool and _Complex are new in C99.) |
| |
| C90 6.5.3, C99 6.7.3: |
| |
| type-qualifier: |
| const |
| restrict |
| volatile |
| address-space-qualifier |
| |
| (restrict is new in C99.) |
| |
| GNU extensions: |
| |
| declaration-specifiers: |
| attributes declaration-specifiers[opt] |
| |
| type-qualifier: |
| address-space |
| |
| address-space: |
| identifier recognized by the target |
| |
| storage-class-specifier: |
| __thread |
| |
| type-specifier: |
| typeof-specifier |
| __int128 |
| _Decimal32 |
| _Decimal64 |
| _Decimal128 |
| _Fract |
| _Accum |
| _Sat |
| |
| (_Fract, _Accum, and _Sat are new from ISO/IEC DTR 18037: |
| http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1169.pdf) |
| |
| Objective-C: |
| |
| type-specifier: |
| class-name objc-protocol-refs[opt] |
| typedef-name objc-protocol-refs |
| objc-protocol-refs |
| */ |
| |
| static void |
| c_parser_declspecs (c_parser *parser, struct c_declspecs *specs, |
| bool scspec_ok, bool typespec_ok, bool start_attr_ok, |
| enum c_lookahead_kind la) |
| { |
| bool attrs_ok = start_attr_ok; |
| bool seen_type = specs->typespec_kind != ctsk_none; |
| |
| if (!typespec_ok) |
| gcc_assert (la == cla_prefer_id); |
| |
| while (c_parser_next_token_is (parser, CPP_NAME) |
| || c_parser_next_token_is (parser, CPP_KEYWORD) |
| || (c_dialect_objc () && c_parser_next_token_is (parser, CPP_LESS))) |
| { |
| struct c_typespec t; |
| tree attrs; |
| location_t loc = c_parser_peek_token (parser)->location; |
| |
| /* If we cannot accept a type, exit if the next token must start |
| one. Also, if we already have seen a tagged definition, |
| a typename would be an error anyway and likely the user |
| has simply forgotten a semicolon, so we exit. */ |
| if ((!typespec_ok || specs->typespec_kind == ctsk_tagdef) |
| && c_parser_next_tokens_start_typename (parser, la) |
| && !c_parser_next_token_is_qualifier (parser)) |
| break; |
| |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| tree value = c_parser_peek_token (parser)->value; |
| c_id_kind kind = c_parser_peek_token (parser)->id_kind; |
| |
| if (kind == C_ID_ADDRSPACE) |
| { |
| addr_space_t as |
| = c_parser_peek_token (parser)->keyword - RID_FIRST_ADDR_SPACE; |
| declspecs_add_addrspace (specs, as); |
| c_parser_consume_token (parser); |
| attrs_ok = true; |
| continue; |
| } |
| |
| gcc_assert (!c_parser_next_token_is_qualifier (parser)); |
| |
| /* If we cannot accept a type, and the next token must start one, |
| exit. Do the same if we already have seen a tagged definition, |
| since it would be an error anyway and likely the user has simply |
| forgotten a semicolon. */ |
| if (seen_type || !c_parser_next_tokens_start_typename (parser, la)) |
| break; |
| |
| /* Now at an unknown typename (C_ID_ID), a C_ID_TYPENAME or |
| a C_ID_CLASSNAME. */ |
| c_parser_consume_token (parser); |
| seen_type = true; |
| attrs_ok = true; |
| if (kind == C_ID_ID) |
| { |
| error ("unknown type name %qE", value); |
| t.kind = ctsk_typedef; |
| t.spec = error_mark_node; |
| } |
| else if (kind == C_ID_TYPENAME |
| && (!c_dialect_objc () |
| || c_parser_next_token_is_not (parser, CPP_LESS))) |
| { |
| t.kind = ctsk_typedef; |
| /* For a typedef name, record the meaning, not the name. |
| In case of 'foo foo, bar;'. */ |
| t.spec = lookup_name (value); |
| } |
| else |
| { |
| tree proto = NULL_TREE; |
| gcc_assert (c_dialect_objc ()); |
| t.kind = ctsk_objc; |
| if (c_parser_next_token_is (parser, CPP_LESS)) |
| proto = c_parser_objc_protocol_refs (parser); |
| t.spec = objc_get_protocol_qualified_type (value, proto); |
| } |
| t.expr = NULL_TREE; |
| t.expr_const_operands = true; |
| declspecs_add_type (loc, specs, t); |
| continue; |
| } |
| if (c_parser_next_token_is (parser, CPP_LESS)) |
| { |
| /* Make "<SomeProtocol>" equivalent to "id <SomeProtocol>" - |
| nisse@lysator.liu.se. */ |
| tree proto; |
| gcc_assert (c_dialect_objc ()); |
| if (!typespec_ok || seen_type) |
| break; |
| proto = c_parser_objc_protocol_refs (parser); |
| t.kind = ctsk_objc; |
| t.spec = objc_get_protocol_qualified_type (NULL_TREE, proto); |
| t.expr = NULL_TREE; |
| t.expr_const_operands = true; |
| declspecs_add_type (loc, specs, t); |
| continue; |
| } |
| gcc_assert (c_parser_next_token_is (parser, CPP_KEYWORD)); |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_STATIC: |
| case RID_EXTERN: |
| case RID_REGISTER: |
| case RID_TYPEDEF: |
| case RID_INLINE: |
| case RID_AUTO: |
| case RID_THREAD: |
| if (!scspec_ok) |
| goto out; |
| attrs_ok = true; |
| /* TODO: Distinguish between function specifiers (inline) |
| and storage class specifiers, either here or in |
| declspecs_add_scspec. */ |
| declspecs_add_scspec (specs, c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| break; |
| case RID_UNSIGNED: |
| case RID_LONG: |
| case RID_INT128: |
| case RID_SHORT: |
| case RID_SIGNED: |
| case RID_COMPLEX: |
| case RID_INT: |
| case RID_CHAR: |
| case RID_FLOAT: |
| case RID_DOUBLE: |
| case RID_VOID: |
| case RID_DFLOAT32: |
| case RID_DFLOAT64: |
| case RID_DFLOAT128: |
| case RID_BOOL: |
| case RID_FRACT: |
| case RID_ACCUM: |
| case RID_SAT: |
| if (!typespec_ok) |
| goto out; |
| attrs_ok = true; |
| seen_type = true; |
| if (c_dialect_objc ()) |
| parser->objc_need_raw_identifier = true; |
| t.kind = ctsk_resword; |
| t.spec = c_parser_peek_token (parser)->value; |
| t.expr = NULL_TREE; |
| t.expr_const_operands = true; |
| declspecs_add_type (loc, specs, t); |
| c_parser_consume_token (parser); |
| break; |
| case RID_ENUM: |
| if (!typespec_ok) |
| goto out; |
| attrs_ok = true; |
| seen_type = true; |
| t = c_parser_enum_specifier (parser); |
| declspecs_add_type (loc, specs, t); |
| break; |
| case RID_STRUCT: |
| case RID_UNION: |
| if (!typespec_ok) |
| goto out; |
| attrs_ok = true; |
| seen_type = true; |
| t = c_parser_struct_or_union_specifier (parser); |
| invoke_plugin_callbacks (PLUGIN_FINISH_TYPE, t.spec); |
| declspecs_add_type (loc, specs, t); |
| break; |
| case RID_TYPEOF: |
| /* ??? The old parser rejected typeof after other type |
| specifiers, but is a syntax error the best way of |
| handling this? */ |
| if (!typespec_ok || seen_type) |
| goto out; |
| attrs_ok = true; |
| seen_type = true; |
| t = c_parser_typeof_specifier (parser); |
| declspecs_add_type (loc, specs, t); |
| break; |
| case RID_CONST: |
| case RID_VOLATILE: |
| case RID_RESTRICT: |
| attrs_ok = true; |
| declspecs_add_qual (specs, c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| break; |
| case RID_ATTRIBUTE: |
| if (!attrs_ok) |
| goto out; |
| attrs = c_parser_attributes (parser); |
| declspecs_add_attrs (specs, attrs); |
| break; |
| default: |
| goto out; |
| } |
| } |
| out: ; |
| } |
| |
| /* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2). |
| |
| enum-specifier: |
| enum attributes[opt] identifier[opt] { enumerator-list } attributes[opt] |
| enum attributes[opt] identifier[opt] { enumerator-list , } attributes[opt] |
| enum attributes[opt] identifier |
| |
| The form with trailing comma is new in C99. The forms with |
| attributes are GNU extensions. In GNU C, we accept any expression |
| without commas in the syntax (assignment expressions, not just |
| conditional expressions); assignment expressions will be diagnosed |
| as non-constant. |
| |
| enumerator-list: |
| enumerator |
| enumerator-list , enumerator |
| |
| enumerator: |
| enumeration-constant |
| enumeration-constant = constant-expression |
| */ |
| |
| static struct c_typespec |
| c_parser_enum_specifier (c_parser *parser) |
| { |
| struct c_typespec ret; |
| tree attrs; |
| tree ident = NULL_TREE; |
| location_t enum_loc; |
| location_t ident_loc = UNKNOWN_LOCATION; /* Quiet warning. */ |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_ENUM)); |
| enum_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| attrs = c_parser_attributes (parser); |
| enum_loc = c_parser_peek_token (parser)->location; |
| /* Set the location in case we create a decl now. */ |
| c_parser_set_source_position_from_token (c_parser_peek_token (parser)); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| ident = c_parser_peek_token (parser)->value; |
| ident_loc = c_parser_peek_token (parser)->location; |
| enum_loc = ident_loc; |
| c_parser_consume_token (parser); |
| } |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| { |
| /* Parse an enum definition. */ |
| struct c_enum_contents the_enum; |
| tree type = start_enum (enum_loc, &the_enum, ident); |
| tree postfix_attrs; |
| /* We chain the enumerators in reverse order, then put them in |
| forward order at the end. */ |
| tree values = NULL_TREE; |
| c_parser_consume_token (parser); |
| while (true) |
| { |
| tree enum_id; |
| tree enum_value; |
| tree enum_decl; |
| bool seen_comma; |
| c_token *token; |
| location_t comma_loc = UNKNOWN_LOCATION; /* Quiet warning. */ |
| location_t decl_loc, value_loc; |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL); |
| values = error_mark_node; |
| break; |
| } |
| token = c_parser_peek_token (parser); |
| enum_id = token->value; |
| /* Set the location in case we create a decl now. */ |
| c_parser_set_source_position_from_token (token); |
| decl_loc = value_loc = token->location; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_EQ)) |
| { |
| c_parser_consume_token (parser); |
| value_loc = c_parser_peek_token (parser)->location; |
| enum_value = c_parser_expr_no_commas (parser, NULL).value; |
| } |
| else |
| enum_value = NULL_TREE; |
| enum_decl = build_enumerator (decl_loc, value_loc, |
| &the_enum, enum_id, enum_value); |
| TREE_CHAIN (enum_decl) = values; |
| values = enum_decl; |
| seen_comma = false; |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| comma_loc = c_parser_peek_token (parser)->location; |
| seen_comma = true; |
| c_parser_consume_token (parser); |
| } |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| if (seen_comma && !flag_isoc99) |
| pedwarn (comma_loc, OPT_pedantic, "comma at end of enumerator list"); |
| c_parser_consume_token (parser); |
| break; |
| } |
| if (!seen_comma) |
| { |
| c_parser_error (parser, "expected %<,%> or %<}%>"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL); |
| values = error_mark_node; |
| break; |
| } |
| } |
| postfix_attrs = c_parser_attributes (parser); |
| ret.spec = finish_enum (type, nreverse (values), |
| chainon (attrs, postfix_attrs)); |
| ret.kind = ctsk_tagdef; |
| ret.expr = NULL_TREE; |
| ret.expr_const_operands = true; |
| return ret; |
| } |
| else if (!ident) |
| { |
| c_parser_error (parser, "expected %<{%>"); |
| ret.spec = error_mark_node; |
| ret.kind = ctsk_tagref; |
| ret.expr = NULL_TREE; |
| ret.expr_const_operands = true; |
| return ret; |
| } |
| ret = parser_xref_tag (ident_loc, ENUMERAL_TYPE, ident); |
| /* In ISO C, enumerated types can be referred to only if already |
| defined. */ |
| if (pedantic && !COMPLETE_TYPE_P (ret.spec)) |
| { |
| gcc_assert (ident); |
| pedwarn (enum_loc, OPT_pedantic, |
| "ISO C forbids forward references to %<enum%> types"); |
| } |
| return ret; |
| } |
| |
| /* Parse a struct or union specifier (C90 6.5.2.1, C99 6.7.2.1). |
| |
| struct-or-union-specifier: |
| struct-or-union attributes[opt] identifier[opt] |
| { struct-contents } attributes[opt] |
| struct-or-union attributes[opt] identifier |
| |
| struct-contents: |
| struct-declaration-list |
| |
| struct-declaration-list: |
| struct-declaration ; |
| struct-declaration-list struct-declaration ; |
| |
| GNU extensions: |
| |
| struct-contents: |
| empty |
| struct-declaration |
| struct-declaration-list struct-declaration |
| |
| struct-declaration-list: |
| struct-declaration-list ; |
| ; |
| |
| (Note that in the syntax here, unlike that in ISO C, the semicolons |
| are included here rather than in struct-declaration, in order to |
| describe the syntax with extra semicolons and missing semicolon at |
| end.) |
| |
| Objective-C: |
| |
| struct-declaration-list: |
| @defs ( class-name ) |
| |
| (Note this does not include a trailing semicolon, but can be |
| followed by further declarations, and gets a pedwarn-if-pedantic |
| when followed by a semicolon.) */ |
| |
| static struct c_typespec |
| c_parser_struct_or_union_specifier (c_parser *parser) |
| { |
| struct c_typespec ret; |
| tree attrs; |
| tree ident = NULL_TREE; |
| location_t struct_loc; |
| location_t ident_loc = UNKNOWN_LOCATION; |
| enum tree_code code; |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_STRUCT: |
| code = RECORD_TYPE; |
| break; |
| case RID_UNION: |
| code = UNION_TYPE; |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| struct_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| attrs = c_parser_attributes (parser); |
| |
| /* Set the location in case we create a decl now. */ |
| c_parser_set_source_position_from_token (c_parser_peek_token (parser)); |
| |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| ident = c_parser_peek_token (parser)->value; |
| ident_loc = c_parser_peek_token (parser)->location; |
| struct_loc = ident_loc; |
| c_parser_consume_token (parser); |
| } |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| { |
| /* Parse a struct or union definition. Start the scope of the |
| tag before parsing components. */ |
| struct c_struct_parse_info *struct_info; |
| tree type = start_struct (struct_loc, code, ident, &struct_info); |
| tree postfix_attrs; |
| /* We chain the components in reverse order, then put them in |
| forward order at the end. Each struct-declaration may |
| declare multiple components (comma-separated), so we must use |
| chainon to join them, although when parsing each |
| struct-declaration we can use TREE_CHAIN directly. |
| |
| The theory behind all this is that there will be more |
| semicolon separated fields than comma separated fields, and |
| so we'll be minimizing the number of node traversals required |
| by chainon. */ |
| tree contents = NULL_TREE; |
| c_parser_consume_token (parser); |
| /* Handle the Objective-C @defs construct, |
| e.g. foo(sizeof(struct{ @defs(ClassName) }));. */ |
| if (c_parser_next_token_is_keyword (parser, RID_AT_DEFS)) |
| { |
| tree name; |
| gcc_assert (c_dialect_objc ()); |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| goto end_at_defs; |
| if (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_token (parser)->id_kind == C_ID_CLASSNAME) |
| { |
| name = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| c_parser_error (parser, "expected class name"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| goto end_at_defs; |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| contents = nreverse (objc_get_class_ivars (name)); |
| } |
| end_at_defs: |
| /* Parse the struct-declarations and semicolons. Problems with |
| semicolons are diagnosed here; empty structures are diagnosed |
| elsewhere. */ |
| while (true) |
| { |
| tree decls; |
| /* Parse any stray semicolon. */ |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, |
| "extra semicolon in struct or union specified"); |
| c_parser_consume_token (parser); |
| continue; |
| } |
| /* Stop if at the end of the struct or union contents. */ |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| c_parser_consume_token (parser); |
| break; |
| } |
| /* Accept #pragmas at struct scope. */ |
| if (c_parser_next_token_is (parser, CPP_PRAGMA)) |
| { |
| c_parser_pragma (parser, pragma_external); |
| continue; |
| } |
| /* Parse some comma-separated declarations, but not the |
| trailing semicolon if any. */ |
| decls = c_parser_struct_declaration (parser); |
| contents = chainon (decls, contents); |
| /* If no semicolon follows, either we have a parse error or |
| are at the end of the struct or union and should |
| pedwarn. */ |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| c_parser_consume_token (parser); |
| else |
| { |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| pedwarn (c_parser_peek_token (parser)->location, 0, |
| "no semicolon at end of struct or union"); |
| else if (parser->error |
| || !c_parser_next_token_starts_declspecs (parser)) |
| { |
| c_parser_error (parser, "expected %<;%>"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL); |
| break; |
| } |
| |
| /* If we come here, we have already emitted an error |
| for an expected `;', identifier or `(', and we also |
| recovered already. Go on with the next field. */ |
| } |
| } |
| postfix_attrs = c_parser_attributes (parser); |
| ret.spec = finish_struct (struct_loc, type, nreverse (contents), |
| chainon (attrs, postfix_attrs), struct_info); |
| ret.kind = ctsk_tagdef; |
| ret.expr = NULL_TREE; |
| ret.expr_const_operands = true; |
| return ret; |
| } |
| else if (!ident) |
| { |
| c_parser_error (parser, "expected %<{%>"); |
| ret.spec = error_mark_node; |
| ret.kind = ctsk_tagref; |
| ret.expr = NULL_TREE; |
| ret.expr_const_operands = true; |
| return ret; |
| } |
| ret = parser_xref_tag (ident_loc, code, ident); |
| return ret; |
| } |
| |
| /* Parse a struct-declaration (C90 6.5.2.1, C99 6.7.2.1), *without* |
| the trailing semicolon. |
| |
| struct-declaration: |
| specifier-qualifier-list struct-declarator-list |
| static_assert-declaration-no-semi |
| |
| specifier-qualifier-list: |
| type-specifier specifier-qualifier-list[opt] |
| type-qualifier specifier-qualifier-list[opt] |
| attributes specifier-qualifier-list[opt] |
| |
| struct-declarator-list: |
| struct-declarator |
| struct-declarator-list , attributes[opt] struct-declarator |
| |
| struct-declarator: |
| declarator attributes[opt] |
| declarator[opt] : constant-expression attributes[opt] |
| |
| GNU extensions: |
| |
| struct-declaration: |
| __extension__ struct-declaration |
| specifier-qualifier-list |
| |
| Unlike the ISO C syntax, semicolons are handled elsewhere. The use |
| of attributes where shown is a GNU extension. In GNU C, we accept |
| any expression without commas in the syntax (assignment |
| expressions, not just conditional expressions); assignment |
| expressions will be diagnosed as non-constant. */ |
| |
| static tree |
| c_parser_struct_declaration (c_parser *parser) |
| { |
| struct c_declspecs *specs; |
| tree prefix_attrs; |
| tree all_prefix_attrs; |
| tree decls; |
| location_t decl_loc; |
| if (c_parser_next_token_is_keyword (parser, RID_EXTENSION)) |
| { |
| int ext; |
| tree decl; |
| ext = disable_extension_diagnostics (); |
| c_parser_consume_token (parser); |
| decl = c_parser_struct_declaration (parser); |
| restore_extension_diagnostics (ext); |
| return decl; |
| } |
| if (c_parser_next_token_is_keyword (parser, RID_STATIC_ASSERT)) |
| { |
| c_parser_static_assert_declaration_no_semi (parser); |
| return NULL_TREE; |
| } |
| specs = build_null_declspecs (); |
| decl_loc = c_parser_peek_token (parser)->location; |
| c_parser_declspecs (parser, specs, false, true, true, cla_nonabstract_decl); |
| if (parser->error) |
| return NULL_TREE; |
| if (!specs->declspecs_seen_p) |
| { |
| c_parser_error (parser, "expected specifier-qualifier-list"); |
| return NULL_TREE; |
| } |
| finish_declspecs (specs); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON) |
| || c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| tree ret; |
| if (specs->typespec_kind == ctsk_none) |
| { |
| pedwarn (decl_loc, OPT_pedantic, |
| "ISO C forbids member declarations with no members"); |
| shadow_tag_warned (specs, pedantic); |
| ret = NULL_TREE; |
| } |
| else |
| { |
| /* Support for unnamed structs or unions as members of |
| structs or unions (which is [a] useful and [b] supports |
| MS P-SDK). */ |
| tree attrs = NULL; |
| |
| ret = grokfield (c_parser_peek_token (parser)->location, |
| build_id_declarator (NULL_TREE), specs, |
| NULL_TREE, &attrs); |
| if (ret) |
| decl_attributes (&ret, attrs, 0); |
| } |
| return ret; |
| } |
| |
| /* Provide better error recovery. Note that a type name here is valid, |
| and will be treated as a field name. */ |
| if (specs->typespec_kind == ctsk_tagdef |
| && TREE_CODE (specs->type) != ENUMERAL_TYPE |
| && c_parser_next_token_starts_declspecs (parser) |
| && !c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected %<;%>, identifier or %<(%>"); |
| parser->error = false; |
| return NULL_TREE; |
| } |
| |
| pending_xref_error (); |
| prefix_attrs = specs->attrs; |
| all_prefix_attrs = prefix_attrs; |
| specs->attrs = NULL_TREE; |
| decls = NULL_TREE; |
| while (true) |
| { |
| /* Declaring one or more declarators or un-named bit-fields. */ |
| struct c_declarator *declarator; |
| bool dummy = false; |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| declarator = build_id_declarator (NULL_TREE); |
| else |
| declarator = c_parser_declarator (parser, |
| specs->typespec_kind != ctsk_none, |
| C_DTR_NORMAL, &dummy); |
| if (declarator == NULL) |
| { |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| break; |
| } |
| if (c_parser_next_token_is (parser, CPP_COLON) |
| || c_parser_next_token_is (parser, CPP_COMMA) |
| || c_parser_next_token_is (parser, CPP_SEMICOLON) |
| || c_parser_next_token_is (parser, CPP_CLOSE_BRACE) |
| || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| { |
| tree postfix_attrs = NULL_TREE; |
| tree width = NULL_TREE; |
| tree d; |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| { |
| c_parser_consume_token (parser); |
| width = c_parser_expr_no_commas (parser, NULL).value; |
| } |
| if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| postfix_attrs = c_parser_attributes (parser); |
| d = grokfield (c_parser_peek_token (parser)->location, |
| declarator, specs, width, &all_prefix_attrs); |
| decl_attributes (&d, chainon (postfix_attrs, |
| all_prefix_attrs), 0); |
| DECL_CHAIN (d) = decls; |
| decls = d; |
| if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| all_prefix_attrs = chainon (c_parser_attributes (parser), |
| prefix_attrs); |
| else |
| all_prefix_attrs = prefix_attrs; |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else if (c_parser_next_token_is (parser, CPP_SEMICOLON) |
| || c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| /* Semicolon consumed in caller. */ |
| break; |
| } |
| else |
| { |
| c_parser_error (parser, "expected %<,%>, %<;%> or %<}%>"); |
| break; |
| } |
| } |
| else |
| { |
| c_parser_error (parser, |
| "expected %<:%>, %<,%>, %<;%>, %<}%> or " |
| "%<__attribute__%>"); |
| break; |
| } |
| } |
| return decls; |
| } |
| |
| /* Parse a typeof specifier (a GNU extension). |
| |
| typeof-specifier: |
| typeof ( expression ) |
| typeof ( type-name ) |
| */ |
| |
| static struct c_typespec |
| c_parser_typeof_specifier (c_parser *parser) |
| { |
| struct c_typespec ret; |
| ret.kind = ctsk_typeof; |
| ret.spec = error_mark_node; |
| ret.expr = NULL_TREE; |
| ret.expr_const_operands = true; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_TYPEOF)); |
| c_parser_consume_token (parser); |
| c_inhibit_evaluation_warnings++; |
| in_typeof++; |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| c_inhibit_evaluation_warnings--; |
| in_typeof--; |
| return ret; |
| } |
| if (c_parser_next_tokens_start_typename (parser, cla_prefer_id)) |
| { |
| struct c_type_name *type = c_parser_type_name (parser); |
| c_inhibit_evaluation_warnings--; |
| in_typeof--; |
| if (type != NULL) |
| { |
| ret.spec = groktypename (type, &ret.expr, &ret.expr_const_operands); |
| pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE)); |
| } |
| } |
| else |
| { |
| bool was_vm; |
| location_t here = c_parser_peek_token (parser)->location; |
| struct c_expr expr = c_parser_expression (parser); |
| c_inhibit_evaluation_warnings--; |
| in_typeof--; |
| if (TREE_CODE (expr.value) == COMPONENT_REF |
| && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1))) |
| error_at (here, "%<typeof%> applied to a bit-field"); |
| mark_exp_read (expr.value); |
| ret.spec = TREE_TYPE (expr.value); |
| was_vm = variably_modified_type_p (ret.spec, NULL_TREE); |
| /* This is returned with the type so that when the type is |
| evaluated, this can be evaluated. */ |
| if (was_vm) |
| ret.expr = c_fully_fold (expr.value, false, &ret.expr_const_operands); |
| pop_maybe_used (was_vm); |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| return ret; |
| } |
| |
| /* Parse a declarator, possibly an abstract declarator (C90 6.5.4, |
| 6.5.5, C99 6.7.5, 6.7.6). If TYPE_SEEN_P then a typedef name may |
| be redeclared; otherwise it may not. KIND indicates which kind of |
| declarator is wanted. Returns a valid declarator except in the |
| case of a syntax error in which case NULL is returned. *SEEN_ID is |
| set to true if an identifier being declared is seen; this is used |
| to diagnose bad forms of abstract array declarators and to |
| determine whether an identifier list is syntactically permitted. |
| |
| declarator: |
| pointer[opt] direct-declarator |
| |
| direct-declarator: |
| identifier |
| ( attributes[opt] declarator ) |
| direct-declarator array-declarator |
| direct-declarator ( parameter-type-list ) |
| direct-declarator ( identifier-list[opt] ) |
| |
| pointer: |
| * type-qualifier-list[opt] |
| * type-qualifier-list[opt] pointer |
| |
| type-qualifier-list: |
| type-qualifier |
| attributes |
| type-qualifier-list type-qualifier |
| type-qualifier-list attributes |
| |
| parameter-type-list: |
| parameter-list |
| parameter-list , ... |
| |
| parameter-list: |
| parameter-declaration |
| parameter-list , parameter-declaration |
| |
| parameter-declaration: |
| declaration-specifiers declarator attributes[opt] |
| declaration-specifiers abstract-declarator[opt] attributes[opt] |
| |
| identifier-list: |
| identifier |
| identifier-list , identifier |
| |
| abstract-declarator: |
| pointer |
| pointer[opt] direct-abstract-declarator |
| |
| direct-abstract-declarator: |
| ( attributes[opt] abstract-declarator ) |
| direct-abstract-declarator[opt] array-declarator |
| direct-abstract-declarator[opt] ( parameter-type-list[opt] ) |
| |
| GNU extensions: |
| |
| direct-declarator: |
| direct-declarator ( parameter-forward-declarations |
| parameter-type-list[opt] ) |
| |
| direct-abstract-declarator: |
| direct-abstract-declarator[opt] ( parameter-forward-declarations |
| parameter-type-list[opt] ) |
| |
| parameter-forward-declarations: |
| parameter-list ; |
| parameter-forward-declarations parameter-list ; |
| |
| The uses of attributes shown above are GNU extensions. |
| |
| Some forms of array declarator are not included in C99 in the |
| syntax for abstract declarators; these are disallowed elsewhere. |
| This may be a defect (DR#289). |
| |
| This function also accepts an omitted abstract declarator as being |
| an abstract declarator, although not part of the formal syntax. */ |
| |
| static struct c_declarator * |
| c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, |
| bool *seen_id) |
| { |
| /* Parse any initial pointer part. */ |
| if (c_parser_next_token_is (parser, CPP_MULT)) |
| { |
| struct c_declspecs *quals_attrs = build_null_declspecs (); |
| struct c_declarator *inner; |
| c_parser_consume_token (parser); |
| c_parser_declspecs (parser, quals_attrs, false, false, true, cla_prefer_id); |
| inner = c_parser_declarator (parser, type_seen_p, kind, seen_id); |
| if (inner == NULL) |
| return NULL; |
| else |
| return make_pointer_declarator (quals_attrs, inner); |
| } |
| /* Now we have a direct declarator, direct abstract declarator or |
| nothing (which counts as a direct abstract declarator here). */ |
| return c_parser_direct_declarator (parser, type_seen_p, kind, seen_id); |
| } |
| |
| /* Parse a direct declarator or direct abstract declarator; arguments |
| as c_parser_declarator. */ |
| |
| static struct c_declarator * |
| c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind, |
| bool *seen_id) |
| { |
| /* The direct declarator must start with an identifier (possibly |
| omitted) or a parenthesized declarator (possibly abstract). In |
| an ordinary declarator, initial parentheses must start a |
| parenthesized declarator. In an abstract declarator or parameter |
| declarator, they could start a parenthesized declarator or a |
| parameter list. To tell which, the open parenthesis and any |
| following attributes must be read. If a declaration specifier |
| follows, then it is a parameter list; if the specifier is a |
| typedef name, there might be an ambiguity about redeclaring it, |
| which is resolved in the direction of treating it as a typedef |
| name. If a close parenthesis follows, it is also an empty |
| parameter list, as the syntax does not permit empty abstract |
| declarators. Otherwise, it is a parenthesized declarator (in |
| which case the analysis may be repeated inside it, recursively). |
| |
| ??? There is an ambiguity in a parameter declaration "int |
| (__attribute__((foo)) x)", where x is not a typedef name: it |
| could be an abstract declarator for a function, or declare x with |
| parentheses. The proper resolution of this ambiguity needs |
| documenting. At present we follow an accident of the old |
| parser's implementation, whereby the first parameter must have |
| some declaration specifiers other than just attributes. Thus as |
| a parameter declaration it is treated as a parenthesized |
| parameter named x, and as an abstract declarator it is |
| rejected. |
| |
| ??? Also following the old parser, attributes inside an empty |
| parameter list are ignored, making it a list not yielding a |
| prototype, rather than giving an error or making it have one |
| parameter with implicit type int. |
| |
| ??? Also following the old parser, typedef names may be |
| redeclared in declarators, but not Objective-C class names. */ |
| |
| if (kind != C_DTR_ABSTRACT |
| && c_parser_next_token_is (parser, CPP_NAME) |
| && ((type_seen_p |
| && (c_parser_peek_token (parser)->id_kind == C_ID_TYPENAME |
| || c_parser_peek_token (parser)->id_kind == C_ID_CLASSNAME)) |
| || c_parser_peek_token (parser)->id_kind == C_ID_ID)) |
| { |
| struct c_declarator *inner |
| = build_id_declarator (c_parser_peek_token (parser)->value); |
| *seen_id = true; |
| inner->id_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| return c_parser_direct_declarator_inner (parser, *seen_id, inner); |
| } |
| |
| if (kind != C_DTR_NORMAL |
| && c_parser_next_token_is (parser, CPP_OPEN_SQUARE)) |
| { |
| struct c_declarator *inner = build_id_declarator (NULL_TREE); |
| return c_parser_direct_declarator_inner (parser, *seen_id, inner); |
| } |
| |
| /* Either we are at the end of an abstract declarator, or we have |
| parentheses. */ |
| |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| tree attrs; |
| struct c_declarator *inner; |
| c_parser_consume_token (parser); |
| attrs = c_parser_attributes (parser); |
| if (kind != C_DTR_NORMAL |
| && (c_parser_next_token_starts_declspecs (parser) |
| || c_parser_next_token_is (parser, CPP_CLOSE_PAREN))) |
| { |
| struct c_arg_info *args |
| = c_parser_parms_declarator (parser, kind == C_DTR_NORMAL, |
| attrs); |
| if (args == NULL) |
| return NULL; |
| else |
| { |
| inner |
| = build_function_declarator (args, |
| build_id_declarator (NULL_TREE)); |
| return c_parser_direct_declarator_inner (parser, *seen_id, |
| inner); |
| } |
| } |
| /* A parenthesized declarator. */ |
| inner = c_parser_declarator (parser, type_seen_p, kind, seen_id); |
| if (inner != NULL && attrs != NULL) |
| inner = build_attrs_declarator (attrs, inner); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| if (inner == NULL) |
| return NULL; |
| else |
| return c_parser_direct_declarator_inner (parser, *seen_id, inner); |
| } |
| else |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| return NULL; |
| } |
| } |
| else |
| { |
| if (kind == C_DTR_NORMAL) |
| { |
| c_parser_error (parser, "expected identifier or %<(%>"); |
| return NULL; |
| } |
| else |
| return build_id_declarator (NULL_TREE); |
| } |
| } |
| |
| /* Parse part of a direct declarator or direct abstract declarator, |
| given that some (in INNER) has already been parsed; ID_PRESENT is |
| true if an identifier is present, false for an abstract |
| declarator. */ |
| |
| static struct c_declarator * |
| c_parser_direct_declarator_inner (c_parser *parser, bool id_present, |
| struct c_declarator *inner) |
| { |
| /* Parse a sequence of array declarators and parameter lists. */ |
| if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE)) |
| { |
| location_t brace_loc = c_parser_peek_token (parser)->location; |
| struct c_declarator *declarator; |
| struct c_declspecs *quals_attrs = build_null_declspecs (); |
| bool static_seen; |
| bool star_seen; |
| tree dimen; |
| c_parser_consume_token (parser); |
| c_parser_declspecs (parser, quals_attrs, false, false, true, cla_prefer_id); |
| static_seen = c_parser_next_token_is_keyword (parser, RID_STATIC); |
| if (static_seen) |
| c_parser_consume_token (parser); |
| if (static_seen && !quals_attrs->declspecs_seen_p) |
| c_parser_declspecs (parser, quals_attrs, false, false, true, cla_prefer_id); |
| if (!quals_attrs->declspecs_seen_p) |
| quals_attrs = NULL; |
| /* If "static" is present, there must be an array dimension. |
| Otherwise, there may be a dimension, "*", or no |
| dimension. */ |
| if (static_seen) |
| { |
| star_seen = false; |
| dimen = c_parser_expr_no_commas (parser, NULL).value; |
| } |
| else |
| { |
| if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE)) |
| { |
| dimen = NULL_TREE; |
| star_seen = false; |
| } |
| else if (c_parser_next_token_is (parser, CPP_MULT)) |
| { |
| if (c_parser_peek_2nd_token (parser)->type == CPP_CLOSE_SQUARE) |
| { |
| dimen = NULL_TREE; |
| star_seen = true; |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| star_seen = false; |
| dimen = c_parser_expr_no_commas (parser, NULL).value; |
| } |
| } |
| else |
| { |
| star_seen = false; |
| dimen = c_parser_expr_no_commas (parser, NULL).value; |
| } |
| } |
| if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE)) |
| c_parser_consume_token (parser); |
| else |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, |
| "expected %<]%>"); |
| return NULL; |
| } |
| if (dimen) |
| mark_exp_read (dimen); |
| declarator = build_array_declarator (brace_loc, dimen, quals_attrs, |
| static_seen, star_seen); |
| if (declarator == NULL) |
| return NULL; |
| inner = set_array_declarator_inner (declarator, inner); |
| return c_parser_direct_declarator_inner (parser, id_present, inner); |
| } |
| else if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| tree attrs; |
| struct c_arg_info *args; |
| c_parser_consume_token (parser); |
| attrs = c_parser_attributes (parser); |
| args = c_parser_parms_declarator (parser, id_present, attrs); |
| if (args == NULL) |
| return NULL; |
| else |
| { |
| inner = build_function_declarator (args, inner); |
| return c_parser_direct_declarator_inner (parser, id_present, inner); |
| } |
| } |
| return inner; |
| } |
| |
| /* Parse a parameter list or identifier list, including the closing |
| parenthesis but not the opening one. ATTRS are the attributes at |
| the start of the list. ID_LIST_OK is true if an identifier list is |
| acceptable; such a list must not have attributes at the start. */ |
| |
| static struct c_arg_info * |
| c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs) |
| { |
| push_scope (); |
| declare_parm_level (); |
| /* If the list starts with an identifier, it is an identifier list. |
| Otherwise, it is either a prototype list or an empty list. */ |
| if (id_list_ok |
| && !attrs |
| && c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_token (parser)->id_kind == C_ID_ID |
| |
| /* Look ahead to detect typos in type names. */ |
| && c_parser_peek_2nd_token (parser)->type != CPP_NAME |
| && c_parser_peek_2nd_token (parser)->type != CPP_MULT |
| && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_PAREN |
| && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_SQUARE) |
| { |
| tree list = NULL_TREE, *nextp = &list; |
| while (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_token (parser)->id_kind == C_ID_ID) |
| { |
| *nextp = build_tree_list (NULL_TREE, |
| c_parser_peek_token (parser)->value); |
| nextp = & TREE_CHAIN (*nextp); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_COMMA)) |
| break; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| break; |
| } |
| } |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| struct c_arg_info *ret = build_arg_info (); |
| ret->types = list; |
| c_parser_consume_token (parser); |
| pop_scope (); |
| return ret; |
| } |
| else |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| pop_scope (); |
| return NULL; |
| } |
| } |
| else |
| { |
| struct c_arg_info *ret = c_parser_parms_list_declarator (parser, attrs); |
| pop_scope (); |
| return ret; |
| } |
| } |
| |
| /* Parse a parameter list (possibly empty), including the closing |
| parenthesis but not the opening one. ATTRS are the attributes at |
| the start of the list. */ |
| |
| static struct c_arg_info * |
| c_parser_parms_list_declarator (c_parser *parser, tree attrs) |
| { |
| bool bad_parm = false; |
| /* ??? Following the old parser, forward parameter declarations may |
| use abstract declarators, and if no real parameter declarations |
| follow the forward declarations then this is not diagnosed. Also |
| note as above that attributes are ignored as the only contents of |
| the parentheses, or as the only contents after forward |
| declarations. */ |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| struct c_arg_info *ret = build_arg_info (); |
| c_parser_consume_token (parser); |
| return ret; |
| } |
| if (c_parser_next_token_is (parser, CPP_ELLIPSIS)) |
| { |
| struct c_arg_info *ret = build_arg_info (); |
| /* Suppress -Wold-style-definition for this case. */ |
| ret->types = error_mark_node; |
| error_at (c_parser_peek_token (parser)->location, |
| "ISO C requires a named argument before %<...%>"); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| return ret; |
| } |
| else |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| return NULL; |
| } |
| } |
| /* Nonempty list of parameters, either terminated with semicolon |
| (forward declarations; recurse) or with close parenthesis (normal |
| function) or with ", ... )" (variadic function). */ |
| while (true) |
| { |
| /* Parse a parameter. */ |
| struct c_parm *parm = c_parser_parameter_declaration (parser, attrs); |
| attrs = NULL_TREE; |
| if (parm == NULL) |
| bad_parm = true; |
| else |
| push_parm_decl (parm); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| tree new_attrs; |
| c_parser_consume_token (parser); |
| mark_forward_parm_decls (); |
| new_attrs = c_parser_attributes (parser); |
| return c_parser_parms_list_declarator (parser, new_attrs); |
| } |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| if (bad_parm) |
| { |
| get_pending_sizes (); |
| return NULL; |
| } |
| else |
| return get_parm_info (false); |
| } |
| if (!c_parser_require (parser, CPP_COMMA, |
| "expected %<;%>, %<,%> or %<)%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| get_pending_sizes (); |
| return NULL; |
| } |
| if (c_parser_next_token_is (parser, CPP_ELLIPSIS)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| if (bad_parm) |
| { |
| get_pending_sizes (); |
| return NULL; |
| } |
| else |
| return get_parm_info (true); |
| } |
| else |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| get_pending_sizes (); |
| return NULL; |
| } |
| } |
| } |
| } |
| |
| /* Parse a parameter declaration. ATTRS are the attributes at the |
| start of the declaration if it is the first parameter. */ |
| |
| static struct c_parm * |
| c_parser_parameter_declaration (c_parser *parser, tree attrs) |
| { |
| struct c_declspecs *specs; |
| struct c_declarator *declarator; |
| tree prefix_attrs; |
| tree postfix_attrs = NULL_TREE; |
| bool dummy = false; |
| if (!c_parser_next_token_starts_declspecs (parser)) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| if (parser->error) |
| return NULL; |
| c_parser_set_source_position_from_token (token); |
| if (c_parser_next_tokens_start_typename (parser, cla_prefer_type)) |
| { |
| error ("unknown type name %qE", token->value); |
| parser->error = true; |
| } |
| /* ??? In some Objective-C cases '...' isn't applicable so there |
| should be a different message. */ |
| else |
| c_parser_error (parser, |
| "expected declaration specifiers or %<...%>"); |
| c_parser_skip_to_end_of_parameter (parser); |
| return NULL; |
| } |
| specs = build_null_declspecs (); |
| if (attrs) |
| { |
| declspecs_add_attrs (specs, attrs); |
| attrs = NULL_TREE; |
| } |
| c_parser_declspecs (parser, specs, true, true, true, cla_nonabstract_decl); |
| finish_declspecs (specs); |
| pending_xref_error (); |
| prefix_attrs = specs->attrs; |
| specs->attrs = NULL_TREE; |
| declarator = c_parser_declarator (parser, |
| specs->typespec_kind != ctsk_none, |
| C_DTR_PARM, &dummy); |
| if (declarator == NULL) |
| { |
| c_parser_skip_until_found (parser, CPP_COMMA, NULL); |
| return NULL; |
| } |
| if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| postfix_attrs = c_parser_attributes (parser); |
| return build_c_parm (specs, chainon (postfix_attrs, prefix_attrs), |
| declarator); |
| } |
| |
| /* Parse a string literal in an asm expression. It should not be |
| translated, and wide string literals are an error although |
| permitted by the syntax. This is a GNU extension. |
| |
| asm-string-literal: |
| string-literal |
| |
| ??? At present, following the old parser, the caller needs to have |
| set lex_untranslated_string to 1. It would be better to follow the |
| C++ parser rather than using this kludge. */ |
| |
| static tree |
| c_parser_asm_string_literal (c_parser *parser) |
| { |
| tree str; |
| if (c_parser_next_token_is (parser, CPP_STRING)) |
| { |
| str = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| else if (c_parser_next_token_is (parser, CPP_WSTRING)) |
| { |
| error_at (c_parser_peek_token (parser)->location, |
| "wide string literal in %<asm%>"); |
| str = build_string (1, ""); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| c_parser_error (parser, "expected string literal"); |
| str = NULL_TREE; |
| } |
| return str; |
| } |
| |
| /* Parse a simple asm expression. This is used in restricted |
| contexts, where a full expression with inputs and outputs does not |
| make sense. This is a GNU extension. |
| |
| simple-asm-expr: |
| asm ( asm-string-literal ) |
| */ |
| |
| static tree |
| c_parser_simple_asm_expr (c_parser *parser) |
| { |
| tree str; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM)); |
| /* ??? Follow the C++ parser rather than using the |
| lex_untranslated_string kludge. */ |
| parser->lex_untranslated_string = true; |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| parser->lex_untranslated_string = false; |
| return NULL_TREE; |
| } |
| str = c_parser_asm_string_literal (parser); |
| parser->lex_untranslated_string = false; |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| return NULL_TREE; |
| } |
| return str; |
| } |
| |
| /* Parse (possibly empty) attributes. This is a GNU extension. |
| |
| attributes: |
| empty |
| attributes attribute |
| |
| attribute: |
| __attribute__ ( ( attribute-list ) ) |
| |
| attribute-list: |
| attrib |
| attribute_list , attrib |
| |
| attrib: |
| empty |
| any-word |
| any-word ( identifier ) |
| any-word ( identifier , nonempty-expr-list ) |
| any-word ( expr-list ) |
| |
| where the "identifier" must not be declared as a type, and |
| "any-word" may be any identifier (including one declared as a |
| type), a reserved word storage class specifier, type specifier or |
| type qualifier. ??? This still leaves out most reserved keywords |
| (following the old parser), shouldn't we include them, and why not |
| allow identifiers declared as types to start the arguments? */ |
| |
| static tree |
| c_parser_attributes (c_parser *parser) |
| { |
| tree attrs = NULL_TREE; |
| while (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| { |
| /* ??? Follow the C++ parser rather than using the |
| lex_untranslated_string kludge. */ |
| parser->lex_untranslated_string = true; |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| parser->lex_untranslated_string = false; |
| return attrs; |
| } |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| parser->lex_untranslated_string = false; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| return attrs; |
| } |
| /* Parse the attribute list. */ |
| while (c_parser_next_token_is (parser, CPP_COMMA) |
| || c_parser_next_token_is (parser, CPP_NAME) |
| || c_parser_next_token_is (parser, CPP_KEYWORD)) |
| { |
| tree attr, attr_name, attr_args; |
| VEC(tree,gc) *expr_list; |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| c_parser_consume_token (parser); |
| continue; |
| } |
| if (c_parser_next_token_is (parser, CPP_KEYWORD)) |
| { |
| /* ??? See comment above about what keywords are |
| accepted here. */ |
| bool ok; |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_STATIC: |
| case RID_UNSIGNED: |
| case RID_LONG: |
| case RID_INT128: |
| case RID_CONST: |
| case RID_EXTERN: |
| case RID_REGISTER: |
| case RID_TYPEDEF: |
| case RID_SHORT: |
| case RID_INLINE: |
| case RID_VOLATILE: |
| case RID_SIGNED: |
| case RID_AUTO: |
| case RID_RESTRICT: |
| case RID_COMPLEX: |
| case RID_THREAD: |
| case RID_INT: |
| case RID_CHAR: |
| case RID_FLOAT: |
| case RID_DOUBLE: |
| case RID_VOID: |
| case RID_DFLOAT32: |
| case RID_DFLOAT64: |
| case RID_DFLOAT128: |
| case RID_BOOL: |
| case RID_FRACT: |
| case RID_ACCUM: |
| case RID_SAT: |
| ok = true; |
| break; |
| default: |
| ok = false; |
| break; |
| } |
| if (!ok) |
| break; |
| /* Accept __attribute__((__const)) as __attribute__((const)) |
| etc. */ |
| attr_name |
| = ridpointers[(int) c_parser_peek_token (parser)->keyword]; |
| } |
| else |
| attr_name = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN)) |
| { |
| attr = build_tree_list (attr_name, NULL_TREE); |
| attrs = chainon (attrs, attr); |
| continue; |
| } |
| c_parser_consume_token (parser); |
| /* Parse the attribute contents. If they start with an |
| identifier which is followed by a comma or close |
| parenthesis, then the arguments start with that |
| identifier; otherwise they are an expression list. |
| In objective-c the identifier may be a classname. */ |
| if (c_parser_next_token_is (parser, CPP_NAME) |
| && (c_parser_peek_token (parser)->id_kind == C_ID_ID |
| || (c_dialect_objc () |
| && c_parser_peek_token (parser)->id_kind == C_ID_CLASSNAME)) |
| && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA) |
| || (c_parser_peek_2nd_token (parser)->type |
| == CPP_CLOSE_PAREN))) |
| { |
| tree arg1 = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| attr_args = build_tree_list (NULL_TREE, arg1); |
| else |
| { |
| tree tree_list; |
| c_parser_consume_token (parser); |
| expr_list = c_parser_expr_list (parser, false, true, NULL); |
| tree_list = build_tree_list_vec (expr_list); |
| attr_args = tree_cons (NULL_TREE, arg1, tree_list); |
| release_tree_vector (expr_list); |
| } |
| } |
| else |
| { |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| attr_args = NULL_TREE; |
| else |
| { |
| expr_list = c_parser_expr_list (parser, false, true, NULL); |
| attr_args = build_tree_list_vec (expr_list); |
| release_tree_vector (expr_list); |
| } |
| } |
| attr = build_tree_list (attr_name, attr_args); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| c_parser_consume_token (parser); |
| else |
| { |
| parser->lex_untranslated_string = false; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| return attrs; |
| } |
| attrs = chainon (attrs, attr); |
| } |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| c_parser_consume_token (parser); |
| else |
| { |
| parser->lex_untranslated_string = false; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| return attrs; |
| } |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| c_parser_consume_token (parser); |
| else |
| { |
| parser->lex_untranslated_string = false; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| return attrs; |
| } |
| parser->lex_untranslated_string = false; |
| } |
| return attrs; |
| } |
| |
| /* Parse a type name (C90 6.5.5, C99 6.7.6). |
| |
| type-name: |
| specifier-qualifier-list abstract-declarator[opt] |
| */ |
| |
| static struct c_type_name * |
| c_parser_type_name (c_parser *parser) |
| { |
| struct c_declspecs *specs = build_null_declspecs (); |
| struct c_declarator *declarator; |
| struct c_type_name *ret; |
| bool dummy = false; |
| c_parser_declspecs (parser, specs, false, true, true, cla_prefer_type); |
| if (!specs->declspecs_seen_p) |
| { |
| c_parser_error (parser, "expected specifier-qualifier-list"); |
| return NULL; |
| } |
| if (specs->type != error_mark_node) |
| { |
| pending_xref_error (); |
| finish_declspecs (specs); |
| } |
| declarator = c_parser_declarator (parser, |
| specs->typespec_kind != ctsk_none, |
| C_DTR_ABSTRACT, &dummy); |
| if (declarator == NULL) |
| return NULL; |
| ret = XOBNEW (&parser_obstack, struct c_type_name); |
| ret->specs = specs; |
| ret->declarator = declarator; |
| return ret; |
| } |
| |
| /* Parse an initializer (C90 6.5.7, C99 6.7.8). |
| |
| initializer: |
| assignment-expression |
| { initializer-list } |
| { initializer-list , } |
| |
| initializer-list: |
| designation[opt] initializer |
| initializer-list , designation[opt] initializer |
| |
| designation: |
| designator-list = |
| |
| designator-list: |
| designator |
| designator-list designator |
| |
| designator: |
| array-designator |
| . identifier |
| |
| array-designator: |
| [ constant-expression ] |
| |
| GNU extensions: |
| |
| initializer: |
| { } |
| |
| designation: |
| array-designator |
| identifier : |
| |
| array-designator: |
| [ constant-expression ... constant-expression ] |
| |
| Any expression without commas is accepted in the syntax for the |
| constant-expressions, with non-constant expressions rejected later. |
| |
| This function is only used for top-level initializers; for nested |
| ones, see c_parser_initval. */ |
| |
| static struct c_expr |
| c_parser_initializer (c_parser *parser) |
| { |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| return c_parser_braced_init (parser, NULL_TREE, false); |
| else |
| { |
| struct c_expr ret; |
| location_t loc = c_parser_peek_token (parser)->location; |
| ret = c_parser_expr_no_commas (parser, NULL); |
| if (TREE_CODE (ret.value) != STRING_CST |
| && TREE_CODE (ret.value) != COMPOUND_LITERAL_EXPR) |
| ret = default_function_array_read_conversion (loc, ret); |
| return ret; |
| } |
| } |
| |
| /* Parse a braced initializer list. TYPE is the type specified for a |
| compound literal, and NULL_TREE for other initializers and for |
| nested braced lists. NESTED_P is true for nested braced lists, |
| false for the list of a compound literal or the list that is the |
| top-level initializer in a declaration. */ |
| |
| static struct c_expr |
| c_parser_braced_init (c_parser *parser, tree type, bool nested_p) |
| { |
| struct c_expr ret; |
| struct obstack braced_init_obstack; |
| location_t brace_loc = c_parser_peek_token (parser)->location; |
| gcc_obstack_init (&braced_init_obstack); |
| gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE)); |
| c_parser_consume_token (parser); |
| if (nested_p) |
| push_init_level (0, &braced_init_obstack); |
| else |
| really_start_incremental_init (type); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| pedwarn (brace_loc, OPT_pedantic, "ISO C forbids empty initializer braces"); |
| } |
| else |
| { |
| /* Parse a non-empty initializer list, possibly with a trailing |
| comma. */ |
| while (true) |
| { |
| c_parser_initelt (parser, &braced_init_obstack); |
| if (parser->error) |
| break; |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| break; |
| } |
| } |
| if (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE)) |
| { |
| ret.value = error_mark_node; |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected %<}%>"); |
| pop_init_level (0, &braced_init_obstack); |
| obstack_free (&braced_init_obstack, NULL); |
| return ret; |
| } |
| c_parser_consume_token (parser); |
| ret = pop_init_level (0, &braced_init_obstack); |
| obstack_free (&braced_init_obstack, NULL); |
| return ret; |
| } |
| |
| /* Parse a nested initializer, including designators. */ |
| |
| static void |
| c_parser_initelt (c_parser *parser, struct obstack * braced_init_obstack) |
| { |
| /* Parse any designator or designator list. A single array |
| designator may have the subsequent "=" omitted in GNU C, but a |
| longer list or a structure member designator may not. */ |
| if (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_COLON) |
| { |
| /* Old-style structure member designator. */ |
| set_init_label (c_parser_peek_token (parser)->value, |
| braced_init_obstack); |
| /* Use the colon as the error location. */ |
| pedwarn (c_parser_peek_2nd_token (parser)->location, OPT_pedantic, |
| "obsolete use of designated initializer with %<:%>"); |
| c_parser_consume_token (parser); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| /* des_seen is 0 if there have been no designators, 1 if there |
| has been a single array designator and 2 otherwise. */ |
| int des_seen = 0; |
| /* Location of a designator. */ |
| location_t des_loc = UNKNOWN_LOCATION; /* Quiet warning. */ |
| while (c_parser_next_token_is (parser, CPP_OPEN_SQUARE) |
| || c_parser_next_token_is (parser, CPP_DOT)) |
| { |
| int des_prev = des_seen; |
| if (!des_seen) |
| des_loc = c_parser_peek_token (parser)->location; |
| if (des_seen < 2) |
| des_seen++; |
| if (c_parser_next_token_is (parser, CPP_DOT)) |
| { |
| des_seen = 2; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| set_init_label (c_parser_peek_token (parser)->value, |
| braced_init_obstack); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| struct c_expr init; |
| init.value = error_mark_node; |
| init.original_code = ERROR_MARK; |
| init.original_type = NULL; |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_COMMA, NULL); |
| process_init_element (init, false, braced_init_obstack); |
| return; |
| } |
| } |
| else |
| { |
| tree first, second; |
| location_t ellipsis_loc = UNKNOWN_LOCATION; /* Quiet warning. */ |
| /* ??? Following the old parser, [ objc-receiver |
| objc-message-args ] is accepted as an initializer, |
| being distinguished from a designator by what follows |
| the first assignment expression inside the square |
| brackets, but after a first array designator a |
| subsequent square bracket is for Objective-C taken to |
| start an expression, using the obsolete form of |
| designated initializer without '=', rather than |
| possibly being a second level of designation: in LALR |
| terms, the '[' is shifted rather than reducing |
| designator to designator-list. */ |
| if (des_prev == 1 && c_dialect_objc ()) |
| { |
| des_seen = des_prev; |
| break; |
| } |
| if (des_prev == 0 && c_dialect_objc ()) |
| { |
| /* This might be an array designator or an |
| Objective-C message expression. If the former, |
| continue parsing here; if the latter, parse the |
| remainder of the initializer given the starting |
| primary-expression. ??? It might make sense to |
| distinguish when des_prev == 1 as well; see |
| previous comment. */ |
| tree rec, args; |
| struct c_expr mexpr; |
| c_parser_consume_token (parser); |
| if (c_parser_peek_token (parser)->type == CPP_NAME |
| && ((c_parser_peek_token (parser)->id_kind |
| == C_ID_TYPENAME) |
| || (c_parser_peek_token (parser)->id_kind |
| == C_ID_CLASSNAME))) |
| { |
| /* Type name receiver. */ |
| tree id = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| rec = objc_get_class_reference (id); |
| goto parse_message_args; |
| } |
| first = c_parser_expr_no_commas (parser, NULL).value; |
| mark_exp_read (first); |
| if (c_parser_next_token_is (parser, CPP_ELLIPSIS) |
| || c_parser_next_token_is (parser, CPP_CLOSE_SQUARE)) |
| goto array_desig_after_first; |
| /* Expression receiver. So far only one part |
| without commas has been parsed; there might be |
| more of the expression. */ |
| rec = first; |
| while (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| struct c_expr next; |
| location_t comma_loc, exp_loc; |
| comma_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| next = c_parser_expr_no_commas (parser, NULL); |
| next = default_function_array_read_conversion (exp_loc, |
| next); |
| rec = build_compound_expr (comma_loc, rec, next.value); |
| } |
| parse_message_args: |
| /* Now parse the objc-message-args. */ |
| args = c_parser_objc_message_args (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, |
| "expected %<]%>"); |
| mexpr.value |
| = objc_build_message_expr (build_tree_list (rec, args)); |
| mexpr.original_code = ERROR_MARK; |
| mexpr.original_type = NULL; |
| /* Now parse and process the remainder of the |
| initializer, starting with this message |
| expression as a primary-expression. */ |
| c_parser_initval (parser, &mexpr, braced_init_obstack); |
| return; |
| } |
| c_parser_consume_token (parser); |
| first = c_parser_expr_no_commas (parser, NULL).value; |
| mark_exp_read (first); |
| array_desig_after_first: |
| if (c_parser_next_token_is (parser, CPP_ELLIPSIS)) |
| { |
| ellipsis_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| second = c_parser_expr_no_commas (parser, NULL).value; |
| mark_exp_read (second); |
| } |
| else |
| second = NULL_TREE; |
| if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE)) |
| { |
| c_parser_consume_token (parser); |
| set_init_index (first, second, braced_init_obstack); |
| if (second) |
| pedwarn (ellipsis_loc, OPT_pedantic, |
| "ISO C forbids specifying range of elements to initialize"); |
| } |
| else |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, |
| "expected %<]%>"); |
| } |
| } |
| if (des_seen >= 1) |
| { |
| if (c_parser_next_token_is (parser, CPP_EQ)) |
| { |
| if (!flag_isoc99) |
| pedwarn (des_loc, OPT_pedantic, |
| "ISO C90 forbids specifying subobject to initialize"); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| if (des_seen == 1) |
| pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, |
| "obsolete use of designated initializer without %<=%>"); |
| else |
| { |
| struct c_expr init; |
| init.value = error_mark_node; |
| init.original_code = ERROR_MARK; |
| init.original_type = NULL; |
| c_parser_error (parser, "expected %<=%>"); |
| c_parser_skip_until_found (parser, CPP_COMMA, NULL); |
| process_init_element (init, false, braced_init_obstack); |
| return; |
| } |
| } |
| } |
| } |
| c_parser_initval (parser, NULL, braced_init_obstack); |
| } |
| |
| /* Parse a nested initializer; as c_parser_initializer but parses |
| initializers within braced lists, after any designators have been |
| applied. If AFTER is not NULL then it is an Objective-C message |
| expression which is the primary-expression starting the |
| initializer. */ |
| |
| static void |
| c_parser_initval (c_parser *parser, struct c_expr *after, |
| struct obstack * braced_init_obstack) |
| { |
| struct c_expr init; |
| gcc_assert (!after || c_dialect_objc ()); |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE) && !after) |
| init = c_parser_braced_init (parser, NULL_TREE, true); |
| else |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| init = c_parser_expr_no_commas (parser, after); |
| if (init.value != NULL_TREE |
| && TREE_CODE (init.value) != STRING_CST |
| && TREE_CODE (init.value) != COMPOUND_LITERAL_EXPR) |
| init = default_function_array_read_conversion (loc, init); |
| } |
| process_init_element (init, false, braced_init_obstack); |
| } |
| |
| /* Parse a compound statement (possibly a function body) (C90 6.6.2, |
| C99 6.8.2). |
| |
| compound-statement: |
| { block-item-list[opt] } |
| { label-declarations block-item-list } |
| |
| block-item-list: |
| block-item |
| block-item-list block-item |
| |
| block-item: |
| nested-declaration |
| statement |
| |
| nested-declaration: |
| declaration |
| |
| GNU extensions: |
| |
| compound-statement: |
| { label-declarations block-item-list } |
| |
| nested-declaration: |
| __extension__ nested-declaration |
| nested-function-definition |
| |
| label-declarations: |
| label-declaration |
| label-declarations label-declaration |
| |
| label-declaration: |
| __label__ identifier-list ; |
| |
| Allowing the mixing of declarations and code is new in C99. The |
| GNU syntax also permits (not shown above) labels at the end of |
| compound statements, which yield an error. We don't allow labels |
| on declarations; this might seem like a natural extension, but |
| there would be a conflict between attributes on the label and |
| prefix attributes on the declaration. ??? The syntax follows the |
| old parser in requiring something after label declarations. |
| Although they are erroneous if the labels declared aren't defined, |
| is it useful for the syntax to be this way? |
| |
| OpenMP: |
| |
| block-item: |
| openmp-directive |
| |
| openmp-directive: |
| barrier-directive |
| flush-directive */ |
| |
| static tree |
| c_parser_compound_statement (c_parser *parser) |
| { |
| tree stmt; |
| location_t brace_loc; |
| brace_loc = c_parser_peek_token (parser)->location; |
| if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) |
| { |
| /* Ensure a scope is entered and left anyway to avoid confusion |
| if we have just prepared to enter a function body. */ |
| stmt = c_begin_compound_stmt (true); |
| c_end_compound_stmt (brace_loc, stmt, true); |
| return error_mark_node; |
| } |
| stmt = c_begin_compound_stmt (true); |
| c_parser_compound_statement_nostart (parser); |
| return c_end_compound_stmt (brace_loc, stmt, true); |
| } |
| |
| /* Parse a compound statement except for the opening brace. This is |
| used for parsing both compound statements and statement expressions |
| (which follow different paths to handling the opening). */ |
| |
| static void |
| c_parser_compound_statement_nostart (c_parser *parser) |
| { |
| bool last_stmt = false; |
| bool last_label = false; |
| bool save_valid_for_pragma = valid_location_for_stdc_pragma_p (); |
| location_t label_loc = UNKNOWN_LOCATION; /* Quiet warning. */ |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| c_parser_consume_token (parser); |
| return; |
| } |
| mark_valid_location_for_stdc_pragma (true); |
| if (c_parser_next_token_is_keyword (parser, RID_LABEL)) |
| { |
| /* Read zero or more forward-declarations for labels that nested |
| functions can jump to. */ |
| mark_valid_location_for_stdc_pragma (false); |
| while (c_parser_next_token_is_keyword (parser, RID_LABEL)) |
| { |
| label_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| /* Any identifiers, including those declared as type names, |
| are OK here. */ |
| while (true) |
| { |
| tree label; |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| break; |
| } |
| label |
| = declare_label (c_parser_peek_token (parser)->value); |
| C_DECLARED_LABEL_FLAG (label) = 1; |
| add_stmt (build_stmt (label_loc, DECL_EXPR, label)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| pedwarn (label_loc, OPT_pedantic, "ISO C forbids label declarations"); |
| } |
| /* We must now have at least one statement, label or declaration. */ |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| mark_valid_location_for_stdc_pragma (save_valid_for_pragma); |
| c_parser_error (parser, "expected declaration or statement"); |
| c_parser_consume_token (parser); |
| return; |
| } |
| while (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE)) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| if (c_parser_next_token_is_keyword (parser, RID_CASE) |
| || c_parser_next_token_is_keyword (parser, RID_DEFAULT) |
| || (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_COLON)) |
| { |
| if (c_parser_next_token_is_keyword (parser, RID_CASE)) |
| label_loc = c_parser_peek_2nd_token (parser)->location; |
| else |
| label_loc = c_parser_peek_token (parser)->location; |
| last_label = true; |
| last_stmt = false; |
| mark_valid_location_for_stdc_pragma (false); |
| c_parser_label (parser); |
| } |
| else if (!last_label |
| && c_parser_next_tokens_start_declaration (parser)) |
| { |
| last_label = false; |
| mark_valid_location_for_stdc_pragma (false); |
| c_parser_declaration_or_fndef (parser, true, true, true, true, true, NULL); |
| if (last_stmt) |
| pedwarn_c90 (loc, |
| (pedantic && !flag_isoc99) |
| ? OPT_pedantic |
| : OPT_Wdeclaration_after_statement, |
| "ISO C90 forbids mixed declarations and code"); |
| last_stmt = false; |
| } |
| else if (!last_label |
| && c_parser_next_token_is_keyword (parser, RID_EXTENSION)) |
| { |
| /* __extension__ can start a declaration, but is also an |
| unary operator that can start an expression. Consume all |
| but the last of a possible series of __extension__ to |
| determine which. */ |
| while (c_parser_peek_2nd_token (parser)->type == CPP_KEYWORD |
| && (c_parser_peek_2nd_token (parser)->keyword |
| == RID_EXTENSION)) |
| c_parser_consume_token (parser); |
| if (c_token_starts_declaration (c_parser_peek_2nd_token (parser))) |
| { |
| int ext; |
| ext = disable_extension_diagnostics (); |
| c_parser_consume_token (parser); |
| last_label = false; |
| mark_valid_location_for_stdc_pragma (false); |
| c_parser_declaration_or_fndef (parser, true, true, true, true, |
| true, NULL); |
| /* Following the old parser, __extension__ does not |
| disable this diagnostic. */ |
| restore_extension_diagnostics (ext); |
| if (last_stmt) |
| pedwarn_c90 (loc, (pedantic && !flag_isoc99) |
| ? OPT_pedantic |
| : OPT_Wdeclaration_after_statement, |
| "ISO C90 forbids mixed declarations and code"); |
| last_stmt = false; |
| } |
| else |
| goto statement; |
| } |
| else if (c_parser_next_token_is (parser, CPP_PRAGMA)) |
| { |
| /* External pragmas, and some omp pragmas, are not associated |
| with regular c code, and so are not to be considered statements |
| syntactically. This ensures that the user doesn't put them |
| places that would turn into syntax errors if the directive |
| were ignored. */ |
| if (c_parser_pragma (parser, pragma_compound)) |
| last_label = false, last_stmt = true; |
| } |
| else if (c_parser_next_token_is (parser, CPP_EOF)) |
| { |
| mark_valid_location_for_stdc_pragma (save_valid_for_pragma); |
| c_parser_error (parser, "expected declaration or statement"); |
| return; |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_ELSE)) |
| { |
| if (parser->in_if_block) |
| { |
| mark_valid_location_for_stdc_pragma (save_valid_for_pragma); |
| error_at (loc, """expected %<}%> before %<else%>"); |
| return; |
| } |
| else |
| { |
| error_at (loc, "%<else%> without a previous %<if%>"); |
| c_parser_consume_token (parser); |
| continue; |
| } |
| } |
| else |
| { |
| statement: |
| last_label = false; |
| last_stmt = true; |
| mark_valid_location_for_stdc_pragma (false); |
| c_parser_statement_after_labels (parser); |
| } |
| |
| parser->error = false; |
| } |
| if (last_label) |
| error_at (label_loc, "label at end of compound statement"); |
| c_parser_consume_token (parser); |
| /* Restore the value we started with. */ |
| mark_valid_location_for_stdc_pragma (save_valid_for_pragma); |
| } |
| |
| /* Parse a label (C90 6.6.1, C99 6.8.1). |
| |
| label: |
| identifier : attributes[opt] |
| case constant-expression : |
| default : |
| |
| GNU extensions: |
| |
| label: |
| case constant-expression ... constant-expression : |
| |
| The use of attributes on labels is a GNU extension. The syntax in |
| GNU C accepts any expressions without commas, non-constant |
| expressions being rejected later. */ |
| |
| static void |
| c_parser_label (c_parser *parser) |
| { |
| location_t loc1 = c_parser_peek_token (parser)->location; |
| tree label = NULL_TREE; |
| if (c_parser_next_token_is_keyword (parser, RID_CASE)) |
| { |
| tree exp1, exp2; |
| c_parser_consume_token (parser); |
| exp1 = c_parser_expr_no_commas (parser, NULL).value; |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| { |
| c_parser_consume_token (parser); |
| label = do_case (loc1, exp1, NULL_TREE); |
| } |
| else if (c_parser_next_token_is (parser, CPP_ELLIPSIS)) |
| { |
| c_parser_consume_token (parser); |
| exp2 = c_parser_expr_no_commas (parser, NULL).value; |
| if (c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| label = do_case (loc1, exp1, exp2); |
| } |
| else |
| c_parser_error (parser, "expected %<:%> or %<...%>"); |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_DEFAULT)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| label = do_case (loc1, NULL_TREE, NULL_TREE); |
| } |
| else |
| { |
| tree name = c_parser_peek_token (parser)->value; |
| tree tlab; |
| tree attrs; |
| location_t loc2 = c_parser_peek_token (parser)->location; |
| gcc_assert (c_parser_next_token_is (parser, CPP_NAME)); |
| c_parser_consume_token (parser); |
| gcc_assert (c_parser_next_token_is (parser, CPP_COLON)); |
| c_parser_consume_token (parser); |
| attrs = c_parser_attributes (parser); |
| tlab = define_label (loc2, name); |
| if (tlab) |
| { |
| decl_attributes (&tlab, attrs, 0); |
| label = add_stmt (build_stmt (loc1, LABEL_EXPR, tlab)); |
| } |
| } |
| if (label) |
| { |
| if (c_parser_next_tokens_start_declaration (parser)) |
| { |
| error_at (c_parser_peek_token (parser)->location, |
| "a label can only be part of a statement and " |
| "a declaration is not a statement"); |
| c_parser_declaration_or_fndef (parser, /*fndef_ok*/ false, |
| /*static_assert_ok*/ true, |
| /*nested*/ true, /*empty_ok*/ false, |
| /*start_attr_ok*/ true, NULL); |
| } |
| } |
| } |
| |
| /* Parse a statement (C90 6.6, C99 6.8). |
| |
| statement: |
| labeled-statement |
| compound-statement |
| expression-statement |
| selection-statement |
| iteration-statement |
| jump-statement |
| |
| labeled-statement: |
| label statement |
| |
| expression-statement: |
| expression[opt] ; |
| |
| selection-statement: |
| if-statement |
| switch-statement |
| |
| iteration-statement: |
| while-statement |
| do-statement |
| for-statement |
| |
| jump-statement: |
| goto identifier ; |
| continue ; |
| break ; |
| return expression[opt] ; |
| |
| GNU extensions: |
| |
| statement: |
| asm-statement |
| |
| jump-statement: |
| goto * expression ; |
| |
| Objective-C: |
| |
| statement: |
| objc-throw-statement |
| objc-try-catch-statement |
| objc-synchronized-statement |
| |
| objc-throw-statement: |
| @throw expression ; |
| @throw ; |
| |
| OpenMP: |
| |
| statement: |
| openmp-construct |
| |
| openmp-construct: |
| parallel-construct |
| for-construct |
| sections-construct |
| single-construct |
| parallel-for-construct |
| parallel-sections-construct |
| master-construct |
| critical-construct |
| atomic-construct |
| ordered-construct |
| |
| parallel-construct: |
| parallel-directive structured-block |
| |
| for-construct: |
| for-directive iteration-statement |
| |
| sections-construct: |
| sections-directive section-scope |
| |
| single-construct: |
| single-directive structured-block |
| |
| parallel-for-construct: |
| parallel-for-directive iteration-statement |
| |
| parallel-sections-construct: |
| parallel-sections-directive section-scope |
| |
| master-construct: |
| master-directive structured-block |
| |
| critical-construct: |
| critical-directive structured-block |
| |
| atomic-construct: |
| atomic-directive expression-statement |
| |
| ordered-construct: |
| ordered-directive structured-block */ |
| |
| static void |
| c_parser_statement (c_parser *parser) |
| { |
| while (c_parser_next_token_is_keyword (parser, RID_CASE) |
| || c_parser_next_token_is_keyword (parser, RID_DEFAULT) |
| || (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_COLON)) |
| c_parser_label (parser); |
| c_parser_statement_after_labels (parser); |
| } |
| |
| /* Parse a statement, other than a labeled statement. */ |
| |
| static void |
| c_parser_statement_after_labels (c_parser *parser) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| tree stmt = NULL_TREE; |
| bool in_if_block = parser->in_if_block; |
| parser->in_if_block = false; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_OPEN_BRACE: |
| add_stmt (c_parser_compound_statement (parser)); |
| break; |
| case CPP_KEYWORD: |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_IF: |
| c_parser_if_statement (parser); |
| break; |
| case RID_SWITCH: |
| c_parser_switch_statement (parser); |
| break; |
| case RID_WHILE: |
| c_parser_while_statement (parser); |
| break; |
| case RID_DO: |
| c_parser_do_statement (parser); |
| break; |
| case RID_FOR: |
| c_parser_for_statement (parser); |
| break; |
| case RID_GOTO: |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| stmt = c_finish_goto_label (loc, |
| c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| } |
| else if (c_parser_next_token_is (parser, CPP_MULT)) |
| { |
| tree val; |
| |
| c_parser_consume_token (parser); |
| val = c_parser_expression (parser).value; |
| mark_exp_read (val); |
| stmt = c_finish_goto_ptr (loc, val); |
| } |
| else |
| c_parser_error (parser, "expected identifier or %<*%>"); |
| goto expect_semicolon; |
| case RID_CONTINUE: |
| c_parser_consume_token (parser); |
| stmt = c_finish_bc_stmt (loc, &c_cont_label, false); |
| goto expect_semicolon; |
| case RID_BREAK: |
| c_parser_consume_token (parser); |
| stmt = c_finish_bc_stmt (loc, &c_break_label, true); |
| goto expect_semicolon; |
| case RID_RETURN: |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| stmt = c_finish_return (loc, NULL_TREE, NULL_TREE); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| struct c_expr expr = c_parser_expression_conv (parser); |
| mark_exp_read (expr.value); |
| stmt = c_finish_return (loc, expr.value, expr.original_type); |
| goto expect_semicolon; |
| } |
| break; |
| case RID_ASM: |
| stmt = c_parser_asm_statement (parser); |
| break; |
| case RID_AT_THROW: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| stmt = objc_build_throw_stmt (loc, NULL_TREE); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| tree expr = c_parser_expression (parser).value; |
| expr = c_fully_fold (expr, false, NULL); |
| stmt = objc_build_throw_stmt (loc, expr); |
| goto expect_semicolon; |
| } |
| break; |
| case RID_AT_TRY: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_try_catch_finally_statement (parser); |
| break; |
| case RID_AT_SYNCHRONIZED: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_objc_synchronized_statement (parser); |
| break; |
| default: |
| goto expr_stmt; |
| } |
| break; |
| case CPP_SEMICOLON: |
| c_parser_consume_token (parser); |
| break; |
| case CPP_CLOSE_PAREN: |
| case CPP_CLOSE_SQUARE: |
| /* Avoid infinite loop in error recovery: |
| c_parser_skip_until_found stops at a closing nesting |
| delimiter without consuming it, but here we need to consume |
| it to proceed further. */ |
| c_parser_error (parser, "expected statement"); |
| c_parser_consume_token (parser); |
| break; |
| case CPP_PRAGMA: |
| c_parser_pragma (parser, pragma_stmt); |
| break; |
| default: |
| expr_stmt: |
| stmt = c_finish_expr_stmt (loc, c_parser_expression_conv (parser).value); |
| expect_semicolon: |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| break; |
| } |
| /* Two cases cannot and do not have line numbers associated: If stmt |
| is degenerate, such as "2;", then stmt is an INTEGER_CST, which |
| cannot hold line numbers. But that's OK because the statement |
| will either be changed to a MODIFY_EXPR during gimplification of |
| the statement expr, or discarded. If stmt was compound, but |
| without new variables, we will have skipped the creation of a |
| BIND and will have a bare STATEMENT_LIST. But that's OK because |
| (recursively) all of the component statements should already have |
| line numbers assigned. ??? Can we discard no-op statements |
| earlier? */ |
| if (CAN_HAVE_LOCATION_P (stmt) |
| && EXPR_LOCATION (stmt) == UNKNOWN_LOCATION) |
| SET_EXPR_LOCATION (stmt, loc); |
| |
| parser->in_if_block = in_if_block; |
| } |
| |
| /* Parse the condition from an if, do, while or for statements. */ |
| |
| static tree |
| c_parser_condition (c_parser *parser) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| tree cond; |
| cond = c_parser_expression_conv (parser).value; |
| cond = c_objc_common_truthvalue_conversion (loc, cond); |
| cond = c_fully_fold (cond, false, NULL); |
| if (warn_sequence_point) |
| verify_sequence_points (cond); |
| return cond; |
| } |
| |
| /* Parse a parenthesized condition from an if, do or while statement. |
| |
| condition: |
| ( expression ) |
| */ |
| static tree |
| c_parser_paren_condition (c_parser *parser) |
| { |
| tree cond; |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return error_mark_node; |
| cond = c_parser_condition (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| return cond; |
| } |
| |
| /* Parse a statement which is a block in C99. */ |
| |
| static tree |
| c_parser_c99_block_statement (c_parser *parser) |
| { |
| tree block = c_begin_compound_stmt (flag_isoc99); |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_statement (parser); |
| return c_end_compound_stmt (loc, block, flag_isoc99); |
| } |
| |
| /* Parse the body of an if statement. This is just parsing a |
| statement but (a) it is a block in C99, (b) we track whether the |
| body is an if statement for the sake of -Wparentheses warnings, (c) |
| we handle an empty body specially for the sake of -Wempty-body |
| warnings, and (d) we call parser_compound_statement directly |
| because c_parser_statement_after_labels resets |
| parser->in_if_block. */ |
| |
| static tree |
| c_parser_if_body (c_parser *parser, bool *if_p) |
| { |
| tree block = c_begin_compound_stmt (flag_isoc99); |
| location_t body_loc = c_parser_peek_token (parser)->location; |
| while (c_parser_next_token_is_keyword (parser, RID_CASE) |
| || c_parser_next_token_is_keyword (parser, RID_DEFAULT) |
| || (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_COLON)) |
| c_parser_label (parser); |
| *if_p = c_parser_next_token_is_keyword (parser, RID_IF); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| add_stmt (build_empty_stmt (loc)); |
| c_parser_consume_token (parser); |
| if (!c_parser_next_token_is_keyword (parser, RID_ELSE)) |
| warning_at (loc, OPT_Wempty_body, |
| "suggest braces around empty body in an %<if%> statement"); |
| } |
| else if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| add_stmt (c_parser_compound_statement (parser)); |
| else |
| c_parser_statement_after_labels (parser); |
| return c_end_compound_stmt (body_loc, block, flag_isoc99); |
| } |
| |
| /* Parse the else body of an if statement. This is just parsing a |
| statement but (a) it is a block in C99, (b) we handle an empty body |
| specially for the sake of -Wempty-body warnings. */ |
| |
| static tree |
| c_parser_else_body (c_parser *parser) |
| { |
| location_t else_loc = c_parser_peek_token (parser)->location; |
| tree block = c_begin_compound_stmt (flag_isoc99); |
| while (c_parser_next_token_is_keyword (parser, RID_CASE) |
| || c_parser_next_token_is_keyword (parser, RID_DEFAULT) |
| || (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_COLON)) |
| c_parser_label (parser); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| warning_at (loc, |
| OPT_Wempty_body, |
| "suggest braces around empty body in an %<else%> statement"); |
| add_stmt (build_empty_stmt (loc)); |
| c_parser_consume_token (parser); |
| } |
| else |
| c_parser_statement_after_labels (parser); |
| return c_end_compound_stmt (else_loc, block, flag_isoc99); |
| } |
| |
| /* Parse an if statement (C90 6.6.4, C99 6.8.4). |
| |
| if-statement: |
| if ( expression ) statement |
| if ( expression ) statement else statement |
| */ |
| |
| static void |
| c_parser_if_statement (c_parser *parser) |
| { |
| tree block; |
| location_t loc; |
| tree cond; |
| bool first_if = false; |
| tree first_body, second_body; |
| bool in_if_block; |
| |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_IF)); |
| c_parser_consume_token (parser); |
| block = c_begin_compound_stmt (flag_isoc99); |
| loc = c_parser_peek_token (parser)->location; |
| cond = c_parser_paren_condition (parser); |
| in_if_block = parser->in_if_block; |
| parser->in_if_block = true; |
| first_body = c_parser_if_body (parser, &first_if); |
| parser->in_if_block = in_if_block; |
| if (c_parser_next_token_is_keyword (parser, RID_ELSE)) |
| { |
| c_parser_consume_token (parser); |
| second_body = c_parser_else_body (parser); |
| } |
| else |
| second_body = NULL_TREE; |
| c_finish_if_stmt (loc, cond, first_body, second_body, first_if); |
| add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); |
| } |
| |
| /* Parse a switch statement (C90 6.6.4, C99 6.8.4). |
| |
| switch-statement: |
| switch (expression) statement |
| */ |
| |
| static void |
| c_parser_switch_statement (c_parser *parser) |
| { |
| tree block, expr, body, save_break; |
| location_t switch_loc = c_parser_peek_token (parser)->location; |
| location_t switch_cond_loc; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_SWITCH)); |
| c_parser_consume_token (parser); |
| block = c_begin_compound_stmt (flag_isoc99); |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| switch_cond_loc = c_parser_peek_token (parser)->location; |
| expr = c_parser_expression (parser).value; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| else |
| { |
| switch_cond_loc = UNKNOWN_LOCATION; |
| expr = error_mark_node; |
| } |
| c_start_case (switch_loc, switch_cond_loc, expr); |
| save_break = c_break_label; |
| c_break_label = NULL_TREE; |
| body = c_parser_c99_block_statement (parser); |
| c_finish_case (body); |
| if (c_break_label) |
| { |
| location_t here = c_parser_peek_token (parser)->location; |
| tree t = build1 (LABEL_EXPR, void_type_node, c_break_label); |
| SET_EXPR_LOCATION (t, here); |
| add_stmt (t); |
| } |
| c_break_label = save_break; |
| add_stmt (c_end_compound_stmt (switch_loc, block, flag_isoc99)); |
| } |
| |
| /* Parse a while statement (C90 6.6.5, C99 6.8.5). |
| |
| while-statement: |
| while (expression) statement |
| */ |
| |
| static void |
| c_parser_while_statement (c_parser *parser) |
| { |
| tree block, cond, body, save_break, save_cont; |
| location_t loc; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE)); |
| c_parser_consume_token (parser); |
| block = c_begin_compound_stmt (flag_isoc99); |
| loc = c_parser_peek_token (parser)->location; |
| cond = c_parser_paren_condition (parser); |
| save_break = c_break_label; |
| c_break_label = NULL_TREE; |
| save_cont = c_cont_label; |
| c_cont_label = NULL_TREE; |
| body = c_parser_c99_block_statement (parser); |
| c_finish_loop (loc, cond, NULL, body, c_break_label, c_cont_label, true); |
| add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); |
| c_break_label = save_break; |
| c_cont_label = save_cont; |
| } |
| |
| /* Parse a do statement (C90 6.6.5, C99 6.8.5). |
| |
| do-statement: |
| do statement while ( expression ) ; |
| */ |
| |
| static void |
| c_parser_do_statement (c_parser *parser) |
| { |
| tree block, cond, body, save_break, save_cont, new_break, new_cont; |
| location_t loc; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| warning_at (c_parser_peek_token (parser)->location, |
| OPT_Wempty_body, |
| "suggest braces around empty body in %<do%> statement"); |
| block = c_begin_compound_stmt (flag_isoc99); |
| loc = c_parser_peek_token (parser)->location; |
| save_break = c_break_label; |
| c_break_label = NULL_TREE; |
| save_cont = c_cont_label; |
| c_cont_label = NULL_TREE; |
| body = c_parser_c99_block_statement (parser); |
| c_parser_require_keyword (parser, RID_WHILE, "expected %<while%>"); |
| new_break = c_break_label; |
| c_break_label = save_break; |
| new_cont = c_cont_label; |
| c_cont_label = save_cont; |
| cond = c_parser_paren_condition (parser); |
| if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| c_finish_loop (loc, cond, NULL, body, new_break, new_cont, false); |
| add_stmt (c_end_compound_stmt (loc, block, flag_isoc99)); |
| } |
| |
| /* Parse a for statement (C90 6.6.5, C99 6.8.5). |
| |
| for-statement: |
| for ( expression[opt] ; expression[opt] ; expression[opt] ) statement |
| for ( nested-declaration expression[opt] ; expression[opt] ) statement |
| |
| The form with a declaration is new in C99. |
| |
| ??? In accordance with the old parser, the declaration may be a |
| nested function, which is then rejected in check_for_loop_decls, |
| but does it make any sense for this to be included in the grammar? |
| Note in particular that the nested function does not include a |
| trailing ';', whereas the "declaration" production includes one. |
| Also, can we reject bad declarations earlier and cheaper than |
| check_for_loop_decls? |
| |
| In Objective-C, there are two additional variants: |
| |
| foreach-statement: |
| for ( expression in expresssion ) statement |
| for ( declaration in expression ) statement |
| |
| This is inconsistent with C, because the second variant is allowed |
| even if c99 is not enabled. |
| |
| The rest of the comment documents these Objective-C foreach-statement. |
| |
| Here is the canonical example of the first variant: |
| for (object in array) { do something with object } |
| we call the first expression ("object") the "object_expression" and |
| the second expression ("array") the "collection_expression". |
| object_expression must be an lvalue of type "id" (a generic Objective-C |
| object) because the loop works by assigning to object_expression the |
| various objects from the collection_expression. collection_expression |
| must evaluate to something of type "id" which responds to the method |
| countByEnumeratingWithState:objects:count:. |
| |
| The canonical example of the second variant is: |
| for (id object in array) { do something with object } |
| which is completely equivalent to |
| { |
| id object; |
| for (object in array) { do something with object } |
| } |
| Note that initizializing 'object' in some way (eg, "for ((object = |
| xxx) in array) { do something with object }") is possibly |
| technically valid, but completely pointless as 'object' will be |
| assigned to something else as soon as the loop starts. We should |
| most likely reject it (TODO). |
| |
| The beginning of the Objective-C foreach-statement looks exactly |
| like the beginning of the for-statement, and we can tell it is a |
| foreach-statement only because the initial declaration or |
| expression is terminated by 'in' instead of ';'. |
| */ |
| |
| static void |
| c_parser_for_statement (c_parser *parser) |
| { |
| tree block, cond, incr, save_break, save_cont, body; |
| /* The following are only used when parsing an ObjC foreach statement. */ |
| tree object_expression; |
| /* Silence the bogus uninitialized warning. */ |
| tree collection_expression = NULL; |
| location_t loc = c_parser_peek_token (parser)->location; |
| location_t for_loc = c_parser_peek_token (parser)->location; |
| bool is_foreach_statement = false; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR)); |
| c_parser_consume_token (parser); |
| /* Open a compound statement in Objective-C as well, just in case this is |
| as foreach expression. */ |
| block = c_begin_compound_stmt (flag_isoc99 || c_dialect_objc ()); |
| cond = error_mark_node; |
| incr = error_mark_node; |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| /* Parse the initialization declaration or expression. */ |
| object_expression = error_mark_node; |
| parser->objc_could_be_foreach_context = c_dialect_objc (); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| parser->objc_could_be_foreach_context = false; |
| c_parser_consume_token (parser); |
| c_finish_expr_stmt (loc, NULL_TREE); |
| } |
| else if (c_parser_next_tokens_start_declaration (parser)) |
| { |
| c_parser_declaration_or_fndef (parser, true, true, true, true, true, |
| &object_expression); |
| parser->objc_could_be_foreach_context = false; |
| |
| if (c_parser_next_token_is_keyword (parser, RID_IN)) |
| { |
| c_parser_consume_token (parser); |
| is_foreach_statement = true; |
| if (check_for_loop_decls (for_loc, true) == NULL_TREE) |
| c_parser_error (parser, "multiple iterating variables in fast enumeration"); |
| } |
| else |
| check_for_loop_decls (for_loc, flag_isoc99); |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION)) |
| { |
| /* __extension__ can start a declaration, but is also an |
| unary operator that can start an expression. Consume all |
| but the last of a possible series of __extension__ to |
| determine which. */ |
| while (c_parser_peek_2nd_token (parser)->type == CPP_KEYWORD |
| && (c_parser_peek_2nd_token (parser)->keyword |
| == RID_EXTENSION)) |
| c_parser_consume_token (parser); |
| if (c_token_starts_declaration (c_parser_peek_2nd_token (parser))) |
| { |
| int ext; |
| ext = disable_extension_diagnostics (); |
| c_parser_consume_token (parser); |
| c_parser_declaration_or_fndef (parser, true, true, true, true, |
| true, &object_expression); |
| parser->objc_could_be_foreach_context = false; |
| |
| restore_extension_diagnostics (ext); |
| if (c_parser_next_token_is_keyword (parser, RID_IN)) |
| { |
| c_parser_consume_token (parser); |
| is_foreach_statement = true; |
| if (check_for_loop_decls (for_loc, true) == NULL_TREE) |
| c_parser_error (parser, "multiple iterating variables in fast enumeration"); |
| } |
| else |
| check_for_loop_decls (for_loc, flag_isoc99); |
| } |
| else |
| goto init_expr; |
| } |
| else |
| { |
| init_expr: |
| { |
| tree init_expression; |
| init_expression = c_parser_expression (parser).value; |
| parser->objc_could_be_foreach_context = false; |
| if (c_parser_next_token_is_keyword (parser, RID_IN)) |
| { |
| c_parser_consume_token (parser); |
| is_foreach_statement = true; |
| if (! lvalue_p (init_expression)) |
| c_parser_error (parser, "invalid iterating variable in fast enumeration"); |
| object_expression = c_fully_fold (init_expression, false, NULL); |
| } |
| else |
| { |
| c_finish_expr_stmt (loc, init_expression); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| } |
| } |
| /* Parse the loop condition. In the case of a foreach |
| statement, there is no loop condition. */ |
| gcc_assert (!parser->objc_could_be_foreach_context); |
| if (!is_foreach_statement) |
| { |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| c_parser_consume_token (parser); |
| cond = NULL_TREE; |
| } |
| else |
| { |
| cond = c_parser_condition (parser); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| } |
| /* Parse the increment expression (the third expression in a |
| for-statement). In the case of a foreach-statement, this is |
| the expression that follows the 'in'. */ |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| if (is_foreach_statement) |
| { |
| c_parser_error (parser, "missing collection in fast enumeration"); |
| collection_expression = error_mark_node; |
| } |
| else |
| incr = c_process_expr_stmt (loc, NULL_TREE); |
| } |
| else |
| { |
| if (is_foreach_statement) |
| collection_expression = c_fully_fold (c_parser_expression (parser).value, |
| false, NULL); |
| else |
| incr = c_process_expr_stmt (loc, c_parser_expression (parser).value); |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| save_break = c_break_label; |
| c_break_label = NULL_TREE; |
| save_cont = c_cont_label; |
| c_cont_label = NULL_TREE; |
| body = c_parser_c99_block_statement (parser); |
| if (is_foreach_statement) |
| objc_finish_foreach_loop (loc, object_expression, collection_expression, body, c_break_label, c_cont_label); |
| else |
| c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true); |
| add_stmt (c_end_compound_stmt (loc, block, flag_isoc99 || c_dialect_objc ())); |
| c_break_label = save_break; |
| c_cont_label = save_cont; |
| } |
| |
| /* Parse an asm statement, a GNU extension. This is a full-blown asm |
| statement with inputs, outputs, clobbers, and volatile tag |
| allowed. |
| |
| asm-statement: |
| asm type-qualifier[opt] ( asm-argument ) ; |
| asm type-qualifier[opt] goto ( asm-goto-argument ) ; |
| |
| asm-argument: |
| asm-string-literal |
| asm-string-literal : asm-operands[opt] |
| asm-string-literal : asm-operands[opt] : asm-operands[opt] |
| asm-string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers[opt] |
| |
| asm-goto-argument: |
| asm-string-literal : : asm-operands[opt] : asm-clobbers[opt] \ |
| : asm-goto-operands |
| |
| Qualifiers other than volatile are accepted in the syntax but |
| warned for. */ |
| |
| static tree |
| c_parser_asm_statement (c_parser *parser) |
| { |
| tree quals, str, outputs, inputs, clobbers, labels, ret; |
| bool simple, is_goto; |
| location_t asm_loc = c_parser_peek_token (parser)->location; |
| int section, nsections; |
| |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_keyword (parser, RID_VOLATILE)) |
| { |
| quals = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_CONST) |
| || c_parser_next_token_is_keyword (parser, RID_RESTRICT)) |
| { |
| warning_at (c_parser_peek_token (parser)->location, |
| 0, |
| "%E qualifier ignored on asm", |
| c_parser_peek_token (parser)->value); |
| quals = NULL_TREE; |
| c_parser_consume_token (parser); |
| } |
| else |
| quals = NULL_TREE; |
| |
| is_goto = false; |
| if (c_parser_next_token_is_keyword (parser, RID_GOTO)) |
| { |
| c_parser_consume_token (parser); |
| is_goto = true; |
| } |
| |
| /* ??? Follow the C++ parser rather than using the |
| lex_untranslated_string kludge. */ |
| parser->lex_untranslated_string = true; |
| ret = NULL; |
| |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| goto error; |
| |
| str = c_parser_asm_string_literal (parser); |
| if (str == NULL_TREE) |
| goto error_close_paren; |
| |
| simple = true; |
| outputs = NULL_TREE; |
| inputs = NULL_TREE; |
| clobbers = NULL_TREE; |
| labels = NULL_TREE; |
| |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN) && !is_goto) |
| goto done_asm; |
| |
| /* Parse each colon-delimited section of operands. */ |
| nsections = 3 + is_goto; |
| for (section = 0; section < nsections; ++section) |
| { |
| if (!c_parser_require (parser, CPP_COLON, |
| is_goto |
| ? "expected %<:%>" |
| : "expected %<:%> or %<)%>")) |
| goto error_close_paren; |
| |
| /* Once past any colon, we're no longer a simple asm. */ |
| simple = false; |
| |
| if ((!c_parser_next_token_is (parser, CPP_COLON) |
| && !c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| || section == 3) |
| switch (section) |
| { |
| case 0: |
| /* For asm goto, we don't allow output operands, but reserve |
| the slot for a future extension that does allow them. */ |
| if (!is_goto) |
| outputs = c_parser_asm_operands (parser, false); |
| break; |
| case 1: |
| inputs = c_parser_asm_operands (parser, true); |
| break; |
| case 2: |
| clobbers = c_parser_asm_clobbers (parser); |
| break; |
| case 3: |
| labels = c_parser_asm_goto_operands (parser); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN) && !is_goto) |
| goto done_asm; |
| } |
| |
| done_asm: |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| goto error; |
| } |
| |
| if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| |
| ret = build_asm_stmt (quals, build_asm_expr (asm_loc, str, outputs, inputs, |
| clobbers, labels, simple)); |
| |
| error: |
| parser->lex_untranslated_string = false; |
| return ret; |
| |
| error_close_paren: |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| goto error; |
| } |
| |
| /* Parse asm operands, a GNU extension. If CONVERT_P (for inputs but |
| not outputs), apply the default conversion of functions and arrays |
| to pointers. |
| |
| asm-operands: |
| asm-operand |
| asm-operands , asm-operand |
| |
| asm-operand: |
| asm-string-literal ( expression ) |
| [ identifier ] asm-string-literal ( expression ) |
| */ |
| |
| static tree |
| c_parser_asm_operands (c_parser *parser, bool convert_p) |
| { |
| tree list = NULL_TREE; |
| location_t loc; |
| while (true) |
| { |
| tree name, str; |
| struct c_expr expr; |
| if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| name = build_string (IDENTIFIER_LENGTH (id), |
| IDENTIFIER_POINTER (id)); |
| } |
| else |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, NULL); |
| return NULL_TREE; |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, |
| "expected %<]%>"); |
| } |
| else |
| name = NULL_TREE; |
| str = c_parser_asm_string_literal (parser); |
| if (str == NULL_TREE) |
| return NULL_TREE; |
| parser->lex_untranslated_string = false; |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| parser->lex_untranslated_string = true; |
| return NULL_TREE; |
| } |
| loc = c_parser_peek_token (parser)->location; |
| expr = c_parser_expression (parser); |
| mark_exp_read (expr.value); |
| if (convert_p) |
| expr = default_function_array_conversion (loc, expr); |
| expr.value = c_fully_fold (expr.value, false, NULL); |
| parser->lex_untranslated_string = true; |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| return NULL_TREE; |
| } |
| list = chainon (list, build_tree_list (build_tree_list (name, str), |
| expr.value)); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| return list; |
| } |
| |
| /* Parse asm clobbers, a GNU extension. |
| |
| asm-clobbers: |
| asm-string-literal |
| asm-clobbers , asm-string-literal |
| */ |
| |
| static tree |
| c_parser_asm_clobbers (c_parser *parser) |
| { |
| tree list = NULL_TREE; |
| while (true) |
| { |
| tree str = c_parser_asm_string_literal (parser); |
| if (str) |
| list = tree_cons (NULL_TREE, str, list); |
| else |
| return NULL_TREE; |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| return list; |
| } |
| |
| /* Parse asm goto labels, a GNU extension. |
| |
| asm-goto-operands: |
| identifier |
| asm-goto-operands , identifier |
| */ |
| |
| static tree |
| c_parser_asm_goto_operands (c_parser *parser) |
| { |
| tree list = NULL_TREE; |
| while (true) |
| { |
| tree name, label; |
| |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_token *tok = c_parser_peek_token (parser); |
| name = tok->value; |
| label = lookup_label_for_goto (tok->location, name); |
| c_parser_consume_token (parser); |
| TREE_USED (label) = 1; |
| } |
| else |
| { |
| c_parser_error (parser, "expected identifier"); |
| return NULL_TREE; |
| } |
| |
| name = build_string (IDENTIFIER_LENGTH (name), |
| IDENTIFIER_POINTER (name)); |
| list = tree_cons (name, label, list); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| return nreverse (list); |
| } |
| } |
| |
| /* Parse an expression other than a compound expression; that is, an |
| assignment expression (C90 6.3.16, C99 6.5.16). If AFTER is not |
| NULL then it is an Objective-C message expression which is the |
| primary-expression starting the expression as an initializer. |
| |
| assignment-expression: |
| conditional-expression |
| unary-expression assignment-operator assignment-expression |
| |
| assignment-operator: one of |
| = *= /= %= += -= <<= >>= &= ^= |= |
| |
| In GNU C we accept any conditional expression on the LHS and |
| diagnose the invalid lvalue rather than producing a syntax |
| error. */ |
| |
| static struct c_expr |
| c_parser_expr_no_commas (c_parser *parser, struct c_expr *after) |
| { |
| struct c_expr lhs, rhs, ret; |
| enum tree_code code; |
| location_t op_location, exp_location; |
| gcc_assert (!after || c_dialect_objc ()); |
| lhs = c_parser_conditional_expression (parser, after); |
| op_location = c_parser_peek_token (parser)->location; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_EQ: |
| code = NOP_EXPR; |
| break; |
| case CPP_MULT_EQ: |
| code = MULT_EXPR; |
| break; |
| case CPP_DIV_EQ: |
| code = TRUNC_DIV_EXPR; |
| break; |
| case CPP_MOD_EQ: |
| code = TRUNC_MOD_EXPR; |
| break; |
| case CPP_PLUS_EQ: |
| code = PLUS_EXPR; |
| break; |
| case CPP_MINUS_EQ: |
| code = MINUS_EXPR; |
| break; |
| case CPP_LSHIFT_EQ: |
| code = LSHIFT_EXPR; |
| break; |
| case CPP_RSHIFT_EQ: |
| code = RSHIFT_EXPR; |
| break; |
| case CPP_AND_EQ: |
| code = BIT_AND_EXPR; |
| break; |
| case CPP_XOR_EQ: |
| code = BIT_XOR_EXPR; |
| break; |
| case CPP_OR_EQ: |
| code = BIT_IOR_EXPR; |
| break; |
| default: |
| return lhs; |
| } |
| c_parser_consume_token (parser); |
| exp_location = c_parser_peek_token (parser)->location; |
| rhs = c_parser_expr_no_commas (parser, NULL); |
| rhs = default_function_array_read_conversion (exp_location, rhs); |
| ret.value = build_modify_expr (op_location, lhs.value, lhs.original_type, |
| code, exp_location, rhs.value, |
| rhs.original_type); |
| if (code == NOP_EXPR) |
| ret.original_code = MODIFY_EXPR; |
| else |
| { |
| TREE_NO_WARNING (ret.value) = 1; |
| ret.original_code = ERROR_MARK; |
| } |
| ret.original_type = NULL; |
| return ret; |
| } |
| |
| /* Parse a conditional expression (C90 6.3.15, C99 6.5.15). If AFTER |
| is not NULL then it is an Objective-C message expression which is |
| the primary-expression starting the expression as an initializer. |
| |
| conditional-expression: |
| logical-OR-expression |
| logical-OR-expression ? expression : conditional-expression |
| |
| GNU extensions: |
| |
| conditional-expression: |
| logical-OR-expression ? : conditional-expression |
| */ |
| |
| static struct c_expr |
| c_parser_conditional_expression (c_parser *parser, struct c_expr *after) |
| { |
| struct c_expr cond, exp1, exp2, ret; |
| location_t cond_loc, colon_loc, middle_loc; |
| |
| gcc_assert (!after || c_dialect_objc ()); |
| |
| cond = c_parser_binary_expression (parser, after); |
| |
| if (c_parser_next_token_is_not (parser, CPP_QUERY)) |
| return cond; |
| cond_loc = c_parser_peek_token (parser)->location; |
| cond = default_function_array_read_conversion (cond_loc, cond); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| { |
| tree eptype = NULL_TREE; |
| |
| middle_loc = c_parser_peek_token (parser)->location; |
| pedwarn (middle_loc, OPT_pedantic, |
| "ISO C forbids omitting the middle term of a ?: expression"); |
| warn_for_omitted_condop (middle_loc, cond.value); |
| if (TREE_CODE (cond.value) == EXCESS_PRECISION_EXPR) |
| { |
| eptype = TREE_TYPE (cond.value); |
| cond.value = TREE_OPERAND (cond.value, 0); |
| } |
| /* Make sure first operand is calculated only once. */ |
| exp1.value = c_save_expr (default_conversion (cond.value)); |
| if (eptype) |
| exp1.value = build1 (EXCESS_PRECISION_EXPR, eptype, exp1.value); |
| exp1.original_type = NULL; |
| cond.value = c_objc_common_truthvalue_conversion (cond_loc, exp1.value); |
| c_inhibit_evaluation_warnings += cond.value == truthvalue_true_node; |
| } |
| else |
| { |
| cond.value |
| = c_objc_common_truthvalue_conversion |
| (cond_loc, default_conversion (cond.value)); |
| c_inhibit_evaluation_warnings += cond.value == truthvalue_false_node; |
| exp1 = c_parser_expression_conv (parser); |
| mark_exp_read (exp1.value); |
| c_inhibit_evaluation_warnings += |
| ((cond.value == truthvalue_true_node) |
| - (cond.value == truthvalue_false_node)); |
| } |
| |
| colon_loc = c_parser_peek_token (parser)->location; |
| if (!c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| { |
| c_inhibit_evaluation_warnings -= cond.value == truthvalue_true_node; |
| ret.value = error_mark_node; |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| return ret; |
| } |
| { |
| location_t exp2_loc = c_parser_peek_token (parser)->location; |
| exp2 = c_parser_conditional_expression (parser, NULL); |
| exp2 = default_function_array_read_conversion (exp2_loc, exp2); |
| } |
| c_inhibit_evaluation_warnings -= cond.value == truthvalue_true_node; |
| ret.value = build_conditional_expr (colon_loc, cond.value, |
| cond.original_code == C_MAYBE_CONST_EXPR, |
| exp1.value, exp1.original_type, |
| exp2.value, exp2.original_type); |
| ret.original_code = ERROR_MARK; |
| if (exp1.value == error_mark_node || exp2.value == error_mark_node) |
| ret.original_type = NULL; |
| else |
| { |
| tree t1, t2; |
| |
| /* If both sides are enum type, the default conversion will have |
| made the type of the result be an integer type. We want to |
| remember the enum types we started with. */ |
| t1 = exp1.original_type ? exp1.original_type : TREE_TYPE (exp1.value); |
| t2 = exp2.original_type ? exp2.original_type : TREE_TYPE (exp2.value); |
| ret.original_type = ((t1 != error_mark_node |
| && t2 != error_mark_node |
| && (TYPE_MAIN_VARIANT (t1) |
| == TYPE_MAIN_VARIANT (t2))) |
| ? t1 |
| : NULL); |
| } |
| return ret; |
| } |
| |
| /* Parse a binary expression; that is, a logical-OR-expression (C90 |
| 6.3.5-6.3.14, C99 6.5.5-6.5.14). If AFTER is not NULL then it is |
| an Objective-C message expression which is the primary-expression |
| starting the expression as an initializer. |
| |
| multiplicative-expression: |
| cast-expression |
| multiplicative-expression * cast-expression |
| multiplicative-expression / cast-expression |
| multiplicative-expression % cast-expression |
| |
| additive-expression: |
| multiplicative-expression |
| additive-expression + multiplicative-expression |
| additive-expression - multiplicative-expression |
| |
| shift-expression: |
| additive-expression |
| shift-expression << additive-expression |
| shift-expression >> additive-expression |
| |
| relational-expression: |
| shift-expression |
| relational-expression < shift-expression |
| relational-expression > shift-expression |
| relational-expression <= shift-expression |
| relational-expression >= shift-expression |
| |
| equality-expression: |
| relational-expression |
| equality-expression == relational-expression |
| equality-expression != relational-expression |
| |
| AND-expression: |
| equality-expression |
| AND-expression & equality-expression |
| |
| exclusive-OR-expression: |
| AND-expression |
| exclusive-OR-expression ^ AND-expression |
| |
| inclusive-OR-expression: |
| exclusive-OR-expression |
| inclusive-OR-expression | exclusive-OR-expression |
| |
| logical-AND-expression: |
| inclusive-OR-expression |
| logical-AND-expression && inclusive-OR-expression |
| |
| logical-OR-expression: |
| logical-AND-expression |
| logical-OR-expression || logical-AND-expression |
| */ |
| |
| static struct c_expr |
| c_parser_binary_expression (c_parser *parser, struct c_expr *after) |
| { |
| /* A binary expression is parsed using operator-precedence parsing, |
| with the operands being cast expressions. All the binary |
| operators are left-associative. Thus a binary expression is of |
| form: |
| |
| E0 op1 E1 op2 E2 ... |
| |
| which we represent on a stack. On the stack, the precedence |
| levels are strictly increasing. When a new operator is |
| encountered of higher precedence than that at the top of the |
| stack, it is pushed; its LHS is the top expression, and its RHS |
| is everything parsed until it is popped. When a new operator is |
| encountered with precedence less than or equal to that at the top |
| of the stack, triples E[i-1] op[i] E[i] are popped and replaced |
| by the result of the operation until the operator at the top of |
| the stack has lower precedence than the new operator or there is |
| only one element on the stack; then the top expression is the LHS |
| of the new operator. In the case of logical AND and OR |
| expressions, we also need to adjust c_inhibit_evaluation_warnings |
| as appropriate when the operators are pushed and popped. */ |
| |
| /* The precedence levels, where 0 is a dummy lowest level used for |
| the bottom of the stack. */ |
| enum prec { |
| PREC_NONE, |
| PREC_LOGOR, |
| PREC_LOGAND, |
| PREC_BITOR, |
| PREC_BITXOR, |
| PREC_BITAND, |
| PREC_EQ, |
| PREC_REL, |
| PREC_SHIFT, |
| PREC_ADD, |
| PREC_MULT, |
| NUM_PRECS |
| }; |
| struct { |
| /* The expression at this stack level. */ |
| struct c_expr expr; |
| /* The precedence of the operator on its left, PREC_NONE at the |
| bottom of the stack. */ |
| enum prec prec; |
| /* The operation on its left. */ |
| enum tree_code op; |
| /* The source location of this operation. */ |
| location_t loc; |
| } stack[NUM_PRECS]; |
| int sp; |
| /* Location of the binary operator. */ |
| location_t binary_loc = UNKNOWN_LOCATION; /* Quiet warning. */ |
| #define POP \ |
| do { \ |
| switch (stack[sp].op) \ |
| { \ |
| case TRUTH_ANDIF_EXPR: \ |
| c_inhibit_evaluation_warnings -= (stack[sp - 1].expr.value \ |
| == truthvalue_false_node); \ |
| break; \ |
| case TRUTH_ORIF_EXPR: \ |
| c_inhibit_evaluation_warnings -= (stack[sp - 1].expr.value \ |
| == truthvalue_true_node); \ |
| break; \ |
| default: \ |
| break; \ |
| } \ |
| stack[sp - 1].expr \ |
| = default_function_array_read_conversion (stack[sp - 1].loc, \ |
| stack[sp - 1].expr); \ |
| stack[sp].expr \ |
| = default_function_array_read_conversion (stack[sp].loc, \ |
| stack[sp].expr); \ |
| stack[sp - 1].expr = parser_build_binary_op (stack[sp].loc, \ |
| stack[sp].op, \ |
| stack[sp - 1].expr, \ |
| stack[sp].expr); \ |
| sp--; \ |
| } while (0) |
| gcc_assert (!after || c_dialect_objc ()); |
| stack[0].loc = c_parser_peek_token (parser)->location; |
| stack[0].expr = c_parser_cast_expression (parser, after); |
| stack[0].prec = PREC_NONE; |
| sp = 0; |
| while (true) |
| { |
| enum prec oprec; |
| enum tree_code ocode; |
| if (parser->error) |
| goto out; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_MULT: |
| oprec = PREC_MULT; |
| ocode = MULT_EXPR; |
| break; |
| case CPP_DIV: |
| oprec = PREC_MULT; |
| ocode = TRUNC_DIV_EXPR; |
| break; |
| case CPP_MOD: |
| oprec = PREC_MULT; |
| ocode = TRUNC_MOD_EXPR; |
| break; |
| case CPP_PLUS: |
| oprec = PREC_ADD; |
| ocode = PLUS_EXPR; |
| break; |
| case CPP_MINUS: |
| oprec = PREC_ADD; |
| ocode = MINUS_EXPR; |
| break; |
| case CPP_LSHIFT: |
| oprec = PREC_SHIFT; |
| ocode = LSHIFT_EXPR; |
| break; |
| case CPP_RSHIFT: |
| oprec = PREC_SHIFT; |
| ocode = RSHIFT_EXPR; |
| break; |
| case CPP_LESS: |
| oprec = PREC_REL; |
| ocode = LT_EXPR; |
| break; |
| case CPP_GREATER: |
| oprec = PREC_REL; |
| ocode = GT_EXPR; |
| break; |
| case CPP_LESS_EQ: |
| oprec = PREC_REL; |
| ocode = LE_EXPR; |
| break; |
| case CPP_GREATER_EQ: |
| oprec = PREC_REL; |
| ocode = GE_EXPR; |
| break; |
| case CPP_EQ_EQ: |
| oprec = PREC_EQ; |
| ocode = EQ_EXPR; |
| break; |
| case CPP_NOT_EQ: |
| oprec = PREC_EQ; |
| ocode = NE_EXPR; |
| break; |
| case CPP_AND: |
| oprec = PREC_BITAND; |
| ocode = BIT_AND_EXPR; |
| break; |
| case CPP_XOR: |
| oprec = PREC_BITXOR; |
| ocode = BIT_XOR_EXPR; |
| break; |
| case CPP_OR: |
| oprec = PREC_BITOR; |
| ocode = BIT_IOR_EXPR; |
| break; |
| case CPP_AND_AND: |
| oprec = PREC_LOGAND; |
| ocode = TRUTH_ANDIF_EXPR; |
| break; |
| case CPP_OR_OR: |
| oprec = PREC_LOGOR; |
| ocode = TRUTH_ORIF_EXPR; |
| break; |
| default: |
| /* Not a binary operator, so end of the binary |
| expression. */ |
| goto out; |
| } |
| binary_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| while (oprec <= stack[sp].prec) |
| POP; |
| switch (ocode) |
| { |
| case TRUTH_ANDIF_EXPR: |
| stack[sp].expr |
| = default_function_array_read_conversion (stack[sp].loc, |
| stack[sp].expr); |
| stack[sp].expr.value = c_objc_common_truthvalue_conversion |
| (stack[sp].loc, default_conversion (stack[sp].expr.value)); |
| c_inhibit_evaluation_warnings += (stack[sp].expr.value |
| == truthvalue_false_node); |
| break; |
| case TRUTH_ORIF_EXPR: |
| stack[sp].expr |
| = default_function_array_read_conversion (stack[sp].loc, |
| stack[sp].expr); |
| stack[sp].expr.value = c_objc_common_truthvalue_conversion |
| (stack[sp].loc, default_conversion (stack[sp].expr.value)); |
| c_inhibit_evaluation_warnings += (stack[sp].expr.value |
| == truthvalue_true_node); |
| break; |
| default: |
| break; |
| } |
| sp++; |
| stack[sp].loc = binary_loc; |
| stack[sp].expr = c_parser_cast_expression (parser, NULL); |
| stack[sp].prec = oprec; |
| stack[sp].op = ocode; |
| stack[sp].loc = binary_loc; |
| } |
| out: |
| while (sp > 0) |
| POP; |
| return stack[0].expr; |
| #undef POP |
| } |
| |
| /* Parse a cast expression (C90 6.3.4, C99 6.5.4). If AFTER is not |
| NULL then it is an Objective-C message expression which is the |
| primary-expression starting the expression as an initializer. |
| |
| cast-expression: |
| unary-expression |
| ( type-name ) unary-expression |
| */ |
| |
| static struct c_expr |
| c_parser_cast_expression (c_parser *parser, struct c_expr *after) |
| { |
| location_t cast_loc = c_parser_peek_token (parser)->location; |
| gcc_assert (!after || c_dialect_objc ()); |
| if (after) |
| return c_parser_postfix_expression_after_primary (parser, |
| cast_loc, *after); |
| /* If the expression begins with a parenthesized type name, it may |
| be either a cast or a compound literal; we need to see whether |
| the next character is '{' to tell the difference. If not, it is |
| an unary expression. Full detection of unknown typenames here |
| would require a 3-token lookahead. */ |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) |
| && c_token_starts_typename (c_parser_peek_2nd_token (parser))) |
| { |
| struct c_type_name *type_name; |
| struct c_expr ret; |
| struct c_expr expr; |
| c_parser_consume_token (parser); |
| type_name = c_parser_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| if (type_name == NULL) |
| { |
| ret.value = error_mark_node; |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| return ret; |
| } |
| |
| /* Save casted types in the function's used types hash table. */ |
| used_types_insert (type_name->specs->type); |
| |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| return c_parser_postfix_expression_after_paren_type (parser, type_name, |
| cast_loc); |
| { |
| location_t expr_loc = c_parser_peek_token (parser)->location; |
| expr = c_parser_cast_expression (parser, NULL); |
| expr = default_function_array_read_conversion (expr_loc, expr); |
| } |
| ret.value = c_cast_expr (cast_loc, type_name, expr.value); |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| return ret; |
| } |
| else |
| return c_parser_unary_expression (parser); |
| } |
| |
| /* Parse an unary expression (C90 6.3.3, C99 6.5.3). |
| |
| unary-expression: |
| postfix-expression |
| ++ unary-expression |
| -- unary-expression |
| unary-operator cast-expression |
| sizeof unary-expression |
| sizeof ( type-name ) |
| |
| unary-operator: one of |
| & * + - ~ ! |
| |
| GNU extensions: |
| |
| unary-expression: |
| __alignof__ unary-expression |
| __alignof__ ( type-name ) |
| && identifier |
| |
| unary-operator: one of |
| __extension__ __real__ __imag__ |
| |
| In addition, the GNU syntax treats ++ and -- as unary operators, so |
| they may be applied to cast expressions with errors for non-lvalues |
| given later. */ |
| |
| static struct c_expr |
| c_parser_unary_expression (c_parser *parser) |
| { |
| int ext; |
| struct c_expr ret, op; |
| location_t op_loc = c_parser_peek_token (parser)->location; |
| location_t exp_loc; |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_PLUS_PLUS: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_read_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, PREINCREMENT_EXPR, op); |
| case CPP_MINUS_MINUS: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_read_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, PREDECREMENT_EXPR, op); |
| case CPP_AND: |
| c_parser_consume_token (parser); |
| op = c_parser_cast_expression (parser, NULL); |
| mark_exp_read (op.value); |
| return parser_build_unary_op (op_loc, ADDR_EXPR, op); |
| case CPP_MULT: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_read_conversion (exp_loc, op); |
| ret.value = build_indirect_ref (op_loc, op.value, RO_UNARY_STAR); |
| return ret; |
| case CPP_PLUS: |
| if (!c_dialect_objc () && !in_system_header) |
| warning_at (op_loc, |
| OPT_Wtraditional, |
| "traditional C rejects the unary plus operator"); |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_read_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, CONVERT_EXPR, op); |
| case CPP_MINUS: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_read_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, NEGATE_EXPR, op); |
| case CPP_COMPL: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_read_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, BIT_NOT_EXPR, op); |
| case CPP_NOT: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_read_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, TRUTH_NOT_EXPR, op); |
| case CPP_AND_AND: |
| /* Refer to the address of a label as a pointer. */ |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| ret.value = finish_label_address_expr |
| (c_parser_peek_token (parser)->value, op_loc); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| c_parser_error (parser, "expected identifier"); |
| ret.value = error_mark_node; |
| } |
| return ret; |
| case CPP_KEYWORD: |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_SIZEOF: |
| return c_parser_sizeof_expression (parser); |
| case RID_ALIGNOF: |
| return c_parser_alignof_expression (parser); |
| case RID_EXTENSION: |
| c_parser_consume_token (parser); |
| ext = disable_extension_diagnostics (); |
| ret = c_parser_cast_expression (parser, NULL); |
| restore_extension_diagnostics (ext); |
| return ret; |
| case RID_REALPART: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, REALPART_EXPR, op); |
| case RID_IMAGPART: |
| c_parser_consume_token (parser); |
| exp_loc = c_parser_peek_token (parser)->location; |
| op = c_parser_cast_expression (parser, NULL); |
| op = default_function_array_conversion (exp_loc, op); |
| return parser_build_unary_op (op_loc, IMAGPART_EXPR, op); |
| default: |
| return c_parser_postfix_expression (parser); |
| } |
| default: |
| return c_parser_postfix_expression (parser); |
| } |
| } |
| |
| /* Parse a sizeof expression. */ |
| |
| static struct c_expr |
| c_parser_sizeof_expression (c_parser *parser) |
| { |
| struct c_expr expr; |
| location_t expr_loc; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_SIZEOF)); |
| c_parser_consume_token (parser); |
| c_inhibit_evaluation_warnings++; |
| in_sizeof++; |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) |
| && c_token_starts_typename (c_parser_peek_2nd_token (parser))) |
| { |
| /* Either sizeof ( type-name ) or sizeof unary-expression |
| starting with a compound literal. */ |
| struct c_type_name *type_name; |
| c_parser_consume_token (parser); |
| expr_loc = c_parser_peek_token (parser)->location; |
| type_name = c_parser_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| if (type_name == NULL) |
| { |
| struct c_expr ret; |
| c_inhibit_evaluation_warnings--; |
| in_sizeof--; |
| ret.value = error_mark_node; |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| return ret; |
| } |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| { |
| expr = c_parser_postfix_expression_after_paren_type (parser, |
| type_name, |
| expr_loc); |
| goto sizeof_expr; |
| } |
| /* sizeof ( type-name ). */ |
| c_inhibit_evaluation_warnings--; |
| in_sizeof--; |
| return c_expr_sizeof_type (expr_loc, type_name); |
| } |
| else |
| { |
| expr_loc = c_parser_peek_token (parser)->location; |
| expr = c_parser_unary_expression (parser); |
| sizeof_expr: |
| c_inhibit_evaluation_warnings--; |
| in_sizeof--; |
| mark_exp_read (expr.value); |
| if (TREE_CODE (expr.value) == COMPONENT_REF |
| && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1))) |
| error_at (expr_loc, "%<sizeof%> applied to a bit-field"); |
| return c_expr_sizeof_expr (expr_loc, expr); |
| } |
| } |
| |
| /* Parse an alignof expression. */ |
| |
| static struct c_expr |
| c_parser_alignof_expression (c_parser *parser) |
| { |
| struct c_expr expr; |
| location_t loc = c_parser_peek_token (parser)->location; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_ALIGNOF)); |
| c_parser_consume_token (parser); |
| c_inhibit_evaluation_warnings++; |
| in_alignof++; |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) |
| && c_token_starts_typename (c_parser_peek_2nd_token (parser))) |
| { |
| /* Either __alignof__ ( type-name ) or __alignof__ |
| unary-expression starting with a compound literal. */ |
| location_t loc; |
| struct c_type_name *type_name; |
| struct c_expr ret; |
| c_parser_consume_token (parser); |
| loc = c_parser_peek_token (parser)->location; |
| type_name = c_parser_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| if (type_name == NULL) |
| { |
| struct c_expr ret; |
| c_inhibit_evaluation_warnings--; |
| in_alignof--; |
| ret.value = error_mark_node; |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| return ret; |
| } |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| { |
| expr = c_parser_postfix_expression_after_paren_type (parser, |
| type_name, |
| loc); |
| goto alignof_expr; |
| } |
| /* alignof ( type-name ). */ |
| c_inhibit_evaluation_warnings--; |
| in_alignof--; |
| ret.value = c_alignof (loc, groktypename (type_name, NULL, NULL)); |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| return ret; |
| } |
| else |
| { |
| struct c_expr ret; |
| expr = c_parser_unary_expression (parser); |
| alignof_expr: |
| mark_exp_read (expr.value); |
| c_inhibit_evaluation_warnings--; |
| in_alignof--; |
| ret.value = c_alignof_expr (loc, expr.value); |
| ret.original_code = ERROR_MARK; |
| ret.original_type = NULL; |
| return ret; |
| } |
| } |
| |
| /* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2). |
| |
| postfix-expression: |
| primary-expression |
| postfix-expression [ expression ] |
| postfix-expression ( argument-expression-list[opt] ) |
| postfix-expression . identifier |
| postfix-expression -> identifier |
| postfix-expression ++ |
| postfix-expression -- |
| ( type-name ) { initializer-list } |
| ( type-name ) { initializer-list , } |
| |
| argument-expression-list: |
| argument-expression |
| argument-expression-list , argument-expression |
| |
| primary-expression: |
| identifier |
| constant |
| string-literal |
| ( expression ) |
| |
| GNU extensions: |
| |
| primary-expression: |
| __func__ |
| (treated as a keyword in GNU C) |
| __FUNCTION__ |
| __PRETTY_FUNCTION__ |
| ( compound-statement ) |
| __builtin_va_arg ( assignment-expression , type-name ) |
| __builtin_offsetof ( type-name , offsetof-member-designator ) |
| __builtin_choose_expr ( assignment-expression , |
| assignment-expression , |
| assignment-expression ) |
| __builtin_types_compatible_p ( type-name , type-name ) |
| |
| offsetof-member-designator: |
| identifier |
| offsetof-member-designator . identifier |
| offsetof-member-designator [ expression ] |
| |
| Objective-C: |
| |
| primary-expression: |
| [ objc-receiver objc-message-args ] |
| @selector ( objc-selector-arg ) |
| @protocol ( identifier ) |
| @encode ( type-name ) |
| objc-string-literal |
| Classname . identifier |
| */ |
| |
| static struct c_expr |
| c_parser_postfix_expression (c_parser *parser) |
| { |
| struct c_expr expr, e1, e2, e3; |
| struct c_type_name *t1, *t2; |
| location_t loc = c_parser_peek_token (parser)->location;; |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_NUMBER: |
| expr.value = c_parser_peek_token (parser)->value; |
| loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (TREE_CODE (expr.value) == FIXED_CST |
| && !targetm.fixed_point_supported_p ()) |
| { |
| error_at (loc, "fixed-point types not supported for this target"); |
| expr.value = error_mark_node; |
| } |
| break; |
| case CPP_CHAR: |
| case CPP_CHAR16: |
| case CPP_CHAR32: |
| case CPP_WCHAR: |
| expr.value = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| break; |
| case CPP_STRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_WSTRING: |
| case CPP_UTF8STRING: |
| expr.value = c_parser_peek_token (parser)->value; |
| expr.original_code = STRING_CST; |
| c_parser_consume_token (parser); |
| break; |
| case CPP_OBJC_STRING: |
| gcc_assert (c_dialect_objc ()); |
| expr.value |
| = objc_build_string_object (c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| break; |
| case CPP_NAME: |
| switch (c_parser_peek_token (parser)->id_kind) |
| { |
| case C_ID_ID: |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| expr.value = build_external_ref (loc, id, |
| (c_parser_peek_token (parser)->type |
| == CPP_OPEN_PAREN), |
| &expr.original_type); |
| break; |
| } |
| case C_ID_CLASSNAME: |
| { |
| /* Here we parse the Objective-C 2.0 Class.name dot |
| syntax. */ |
| tree class_name = c_parser_peek_token (parser)->value; |
| tree component; |
| c_parser_consume_token (parser); |
| gcc_assert (c_dialect_objc ()); |
| if (!c_parser_require (parser, CPP_DOT, "expected %<.%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| expr.value = error_mark_node; |
| break; |
| } |
| component = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| expr.value = objc_build_class_component_ref (class_name, |
| component); |
| break; |
| } |
| default: |
| c_parser_error (parser, "expected expression"); |
| expr.value = error_mark_node; |
| break; |
| } |
| break; |
| case CPP_OPEN_PAREN: |
| /* A parenthesized expression, statement expression or compound |
| literal. */ |
| if (c_parser_peek_2nd_token (parser)->type == CPP_OPEN_BRACE) |
| { |
| /* A statement expression. */ |
| tree stmt; |
| location_t brace_loc; |
| c_parser_consume_token (parser); |
| brace_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (cur_stmt_list == NULL) |
| { |
| error_at (loc, "braced-group within expression allowed " |
| "only inside a function"); |
| parser->error = true; |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| expr.value = error_mark_node; |
| break; |
| } |
| stmt = c_begin_stmt_expr (); |
| c_parser_compound_statement_nostart (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| pedwarn (loc, OPT_pedantic, |
| "ISO C forbids braced-groups within expressions"); |
| expr.value = c_finish_stmt_expr (brace_loc, stmt); |
| mark_exp_read (expr.value); |
| } |
| else if (c_token_starts_typename (c_parser_peek_2nd_token (parser))) |
| { |
| /* A compound literal. ??? Can we actually get here rather |
| than going directly to |
| c_parser_postfix_expression_after_paren_type from |
| elsewhere? */ |
| location_t loc; |
| struct c_type_name *type_name; |
| c_parser_consume_token (parser); |
| loc = c_parser_peek_token (parser)->location; |
| type_name = c_parser_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| if (type_name == NULL) |
| { |
| expr.value = error_mark_node; |
| } |
| else |
| expr = c_parser_postfix_expression_after_paren_type (parser, |
| type_name, |
| loc); |
| } |
| else |
| { |
| /* A parenthesized expression. */ |
| c_parser_consume_token (parser); |
| expr = c_parser_expression (parser); |
| if (TREE_CODE (expr.value) == MODIFY_EXPR) |
| TREE_NO_WARNING (expr.value) = 1; |
| if (expr.original_code != C_MAYBE_CONST_EXPR) |
| expr.original_code = ERROR_MARK; |
| /* Don't change EXPR.ORIGINAL_TYPE. */ |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| } |
| break; |
| case CPP_KEYWORD: |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_FUNCTION_NAME: |
| case RID_PRETTY_FUNCTION_NAME: |
| case RID_C99_FUNCTION_NAME: |
| expr.value = fname_decl (loc, |
| c_parser_peek_token (parser)->keyword, |
| c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| break; |
| case RID_VA_ARG: |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| e1 = c_parser_expr_no_commas (parser, NULL); |
| mark_exp_read (e1.value); |
| e1.value = c_fully_fold (e1.value, false, NULL); |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| expr.value = error_mark_node; |
| break; |
| } |
| loc = c_parser_peek_token (parser)->location; |
| t1 = c_parser_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| if (t1 == NULL) |
| { |
| expr.value = error_mark_node; |
| } |
| else |
| { |
| tree type_expr = NULL_TREE; |
| expr.value = c_build_va_arg (loc, e1.value, |
| groktypename (t1, &type_expr, NULL)); |
| if (type_expr) |
| { |
| expr.value = build2 (C_MAYBE_CONST_EXPR, |
| TREE_TYPE (expr.value), type_expr, |
| expr.value); |
| C_MAYBE_CONST_EXPR_NON_CONST (expr.value) = true; |
| } |
| } |
| break; |
| case RID_OFFSETOF: |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| t1 = c_parser_type_name (parser); |
| if (t1 == NULL) |
| parser->error = true; |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| gcc_assert (parser->error); |
| if (parser->error) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| expr.value = error_mark_node; |
| break; |
| } |
| |
| { |
| tree type = groktypename (t1, NULL, NULL); |
| tree offsetof_ref; |
| if (type == error_mark_node) |
| offsetof_ref = error_mark_node; |
| else |
| { |
| offsetof_ref = build1 (INDIRECT_REF, type, null_pointer_node); |
| SET_EXPR_LOCATION (offsetof_ref, loc); |
| } |
| /* Parse the second argument to __builtin_offsetof. We |
| must have one identifier, and beyond that we want to |
| accept sub structure and sub array references. */ |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| offsetof_ref = build_component_ref |
| (loc, offsetof_ref, c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| while (c_parser_next_token_is (parser, CPP_DOT) |
| || c_parser_next_token_is (parser, |
| CPP_OPEN_SQUARE) |
| || c_parser_next_token_is (parser, |
| CPP_DEREF)) |
| { |
| if (c_parser_next_token_is (parser, CPP_DEREF)) |
| { |
| loc = c_parser_peek_token (parser)->location; |
| offsetof_ref = build_array_ref (loc, |
| offsetof_ref, |
| integer_zero_node); |
| goto do_dot; |
| } |
| else if (c_parser_next_token_is (parser, CPP_DOT)) |
| { |
| do_dot: |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, |
| CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| break; |
| } |
| offsetof_ref = build_component_ref |
| (loc, offsetof_ref, |
| c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| tree idx; |
| loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| idx = c_parser_expression (parser).value; |
| idx = c_fully_fold (idx, false, NULL); |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, |
| "expected %<]%>"); |
| offsetof_ref = build_array_ref (loc, offsetof_ref, idx); |
| } |
| } |
| } |
| else |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| expr.value = fold_offsetof (offsetof_ref, NULL_TREE); |
| } |
| break; |
| case RID_CHOOSE_EXPR: |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| loc = c_parser_peek_token (parser)->location; |
| e1 = c_parser_expr_no_commas (parser, NULL); |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| expr.value = error_mark_node; |
| break; |
| } |
| e2 = c_parser_expr_no_commas (parser, NULL); |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| expr.value = error_mark_node; |
| break; |
| } |
| e3 = c_parser_expr_no_commas (parser, NULL); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| { |
| tree c; |
| |
| c = e1.value; |
| mark_exp_read (e2.value); |
| mark_exp_read (e3.value); |
| if (TREE_CODE (c) != INTEGER_CST |
| || !INTEGRAL_TYPE_P (TREE_TYPE (c))) |
| error_at (loc, |
| "first argument to %<__builtin_choose_expr%> not" |
| " a constant"); |
| constant_expression_warning (c); |
| expr = integer_zerop (c) ? e3 : e2; |
| } |
| break; |
| case RID_TYPES_COMPATIBLE_P: |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| t1 = c_parser_type_name (parser); |
| if (t1 == NULL) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| expr.value = error_mark_node; |
| break; |
| } |
| t2 = c_parser_type_name (parser); |
| if (t2 == NULL) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| { |
| tree e1, e2; |
| |
| e1 = TYPE_MAIN_VARIANT (groktypename (t1, NULL, NULL)); |
| e2 = TYPE_MAIN_VARIANT (groktypename (t2, NULL, NULL)); |
| |
| expr.value |
| = comptypes (e1, e2) ? integer_one_node : integer_zero_node; |
| } |
| break; |
| case RID_AT_SELECTOR: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| { |
| tree sel = c_parser_objc_selector_arg (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| expr.value = objc_build_selector_expr (loc, sel); |
| } |
| break; |
| case RID_AT_PROTOCOL: |
| gcc_assert (c_dialect_objc ()); |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| expr.value = error_mark_node; |
| break; |
| } |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| expr.value = objc_build_protocol_expr (id); |
| } |
| break; |
| case RID_AT_ENCODE: |
| /* Extension to support C-structures in the archiver. */ |
| gcc_assert (c_dialect_objc ()); |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr.value = error_mark_node; |
| break; |
| } |
| t1 = c_parser_type_name (parser); |
| if (t1 == NULL) |
| { |
| expr.value = error_mark_node; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| break; |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| { |
| tree type = groktypename (t1, NULL, NULL); |
| expr.value = objc_build_encode_expr (type); |
| } |
| break; |
| default: |
| c_parser_error (parser, "expected expression"); |
| expr.value = error_mark_node; |
| break; |
| } |
| break; |
| case CPP_OPEN_SQUARE: |
| if (c_dialect_objc ()) |
| { |
| tree receiver, args; |
| c_parser_consume_token (parser); |
| receiver = c_parser_objc_receiver (parser); |
| args = c_parser_objc_message_args (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, |
| "expected %<]%>"); |
| expr.value = objc_build_message_expr (build_tree_list (receiver, |
| args)); |
| break; |
| } |
| /* Else fall through to report error. */ |
| default: |
| c_parser_error (parser, "expected expression"); |
| expr.value = error_mark_node; |
| break; |
| } |
| return c_parser_postfix_expression_after_primary (parser, loc, expr); |
| } |
| |
| /* Parse a postfix expression after a parenthesized type name: the |
| brace-enclosed initializer of a compound literal, possibly followed |
| by some postfix operators. This is separate because it is not |
| possible to tell until after the type name whether a cast |
| expression has a cast or a compound literal, or whether the operand |
| of sizeof is a parenthesized type name or starts with a compound |
| literal. TYPE_LOC is the location where TYPE_NAME starts--the |
| location of the first token after the parentheses around the type |
| name. */ |
| |
| static struct c_expr |
| c_parser_postfix_expression_after_paren_type (c_parser *parser, |
| struct c_type_name *type_name, |
| location_t type_loc) |
| { |
| tree type; |
| struct c_expr init; |
| bool non_const; |
| struct c_expr expr; |
| location_t start_loc; |
| tree type_expr = NULL_TREE; |
| bool type_expr_const = true; |
| check_compound_literal_type (type_loc, type_name); |
| start_init (NULL_TREE, NULL, 0); |
| type = groktypename (type_name, &type_expr, &type_expr_const); |
| start_loc = c_parser_peek_token (parser)->location; |
| if (type != error_mark_node && C_TYPE_VARIABLE_SIZE (type)) |
| { |
| error_at (type_loc, "compound literal has variable size"); |
| type = error_mark_node; |
| } |
| init = c_parser_braced_init (parser, type, false); |
| finish_init (); |
| maybe_warn_string_init (type, init); |
| |
| if (type != error_mark_node |
| && !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (type)) |
| && current_function_decl) |
| { |
| error ("compound literal qualified by address-space qualifier"); |
| type = error_mark_node; |
| } |
| |
| if (!flag_isoc99) |
| pedwarn (start_loc, OPT_pedantic, "ISO C90 forbids compound literals"); |
| non_const = ((init.value && TREE_CODE (init.value) == CONSTRUCTOR) |
| ? CONSTRUCTOR_NON_CONST (init.value) |
| : init.original_code == C_MAYBE_CONST_EXPR); |
| non_const |= !type_expr_const; |
| expr.value = build_compound_literal (start_loc, type, init.value, non_const); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| if (type_expr) |
| { |
| if (TREE_CODE (expr.value) == C_MAYBE_CONST_EXPR) |
| { |
| gcc_assert (C_MAYBE_CONST_EXPR_PRE (expr.value) == NULL_TREE); |
| C_MAYBE_CONST_EXPR_PRE (expr.value) = type_expr; |
| } |
| else |
| { |
| gcc_assert (!non_const); |
| expr.value = build2 (C_MAYBE_CONST_EXPR, type, |
| type_expr, expr.value); |
| } |
| } |
| return c_parser_postfix_expression_after_primary (parser, start_loc, expr); |
| } |
| |
| /* Parse a postfix expression after the initial primary or compound |
| literal; that is, parse a series of postfix operators. |
| |
| EXPR_LOC is the location of the primary expression. */ |
| |
| static struct c_expr |
| c_parser_postfix_expression_after_primary (c_parser *parser, |
| location_t expr_loc, |
| struct c_expr expr) |
| { |
| struct c_expr orig_expr; |
| tree ident, idx; |
| VEC(tree,gc) *exprlist; |
| VEC(tree,gc) *origtypes; |
| while (true) |
| { |
| location_t op_loc = c_parser_peek_token (parser)->location; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_OPEN_SQUARE: |
| /* Array reference. */ |
| c_parser_consume_token (parser); |
| idx = c_parser_expression (parser).value; |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, |
| "expected %<]%>"); |
| expr.value = build_array_ref (op_loc, expr.value, idx); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| break; |
| case CPP_OPEN_PAREN: |
| /* Function call. */ |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| exprlist = NULL; |
| else |
| exprlist = c_parser_expr_list (parser, true, false, &origtypes); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| orig_expr = expr; |
| mark_exp_read (expr.value); |
| /* FIXME diagnostics: Ideally we want the FUNCNAME, not the |
| "(" after the FUNCNAME, which is what we have now. */ |
| expr.value = build_function_call_vec (op_loc, expr.value, exprlist, |
| origtypes); |
| expr.original_code = ERROR_MARK; |
| if (TREE_CODE (expr.value) == INTEGER_CST |
| && TREE_CODE (orig_expr.value) == FUNCTION_DECL |
| && DECL_BUILT_IN_CLASS (orig_expr.value) == BUILT_IN_NORMAL |
| && DECL_FUNCTION_CODE (orig_expr.value) == BUILT_IN_CONSTANT_P) |
| expr.original_code = C_MAYBE_CONST_EXPR; |
| expr.original_type = NULL; |
| if (exprlist != NULL) |
| { |
| release_tree_vector (exprlist); |
| release_tree_vector (origtypes); |
| } |
| break; |
| case CPP_DOT: |
| /* Structure element reference. */ |
| c_parser_consume_token (parser); |
| expr = default_function_array_conversion (expr_loc, expr); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| ident = c_parser_peek_token (parser)->value; |
| else |
| { |
| c_parser_error (parser, "expected identifier"); |
| expr.value = error_mark_node; |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| return expr; |
| } |
| c_parser_consume_token (parser); |
| expr.value = build_component_ref (op_loc, expr.value, ident); |
| expr.original_code = ERROR_MARK; |
| if (TREE_CODE (expr.value) != COMPONENT_REF) |
| expr.original_type = NULL; |
| else |
| { |
| /* Remember the original type of a bitfield. */ |
| tree field = TREE_OPERAND (expr.value, 1); |
| if (TREE_CODE (field) != FIELD_DECL) |
| expr.original_type = NULL; |
| else |
| expr.original_type = DECL_BIT_FIELD_TYPE (field); |
| } |
| break; |
| case CPP_DEREF: |
| /* Structure element reference. */ |
| c_parser_consume_token (parser); |
| expr = default_function_array_conversion (expr_loc, expr); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| ident = c_parser_peek_token (parser)->value; |
| else |
| { |
| c_parser_error (parser, "expected identifier"); |
| expr.value = error_mark_node; |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| return expr; |
| } |
| c_parser_consume_token (parser); |
| expr.value = build_component_ref (op_loc, |
| build_indirect_ref (op_loc, |
| expr.value, |
| RO_ARROW), |
| ident); |
| expr.original_code = ERROR_MARK; |
| if (TREE_CODE (expr.value) != COMPONENT_REF) |
| expr.original_type = NULL; |
| else |
| { |
| /* Remember the original type of a bitfield. */ |
| tree field = TREE_OPERAND (expr.value, 1); |
| if (TREE_CODE (field) != FIELD_DECL) |
| expr.original_type = NULL; |
| else |
| expr.original_type = DECL_BIT_FIELD_TYPE (field); |
| } |
| break; |
| case CPP_PLUS_PLUS: |
| /* Postincrement. */ |
| c_parser_consume_token (parser); |
| expr = default_function_array_read_conversion (expr_loc, expr); |
| expr.value = build_unary_op (op_loc, |
| POSTINCREMENT_EXPR, expr.value, 0); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| break; |
| case CPP_MINUS_MINUS: |
| /* Postdecrement. */ |
| c_parser_consume_token (parser); |
| expr = default_function_array_read_conversion (expr_loc, expr); |
| expr.value = build_unary_op (op_loc, |
| POSTDECREMENT_EXPR, expr.value, 0); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| break; |
| default: |
| return expr; |
| } |
| } |
| } |
| |
| /* Parse an expression (C90 6.3.17, C99 6.5.17). |
| |
| expression: |
| assignment-expression |
| expression , assignment-expression |
| */ |
| |
| static struct c_expr |
| c_parser_expression (c_parser *parser) |
| { |
| struct c_expr expr; |
| expr = c_parser_expr_no_commas (parser, NULL); |
| while (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| struct c_expr next; |
| tree lhsval; |
| location_t loc = c_parser_peek_token (parser)->location; |
| location_t expr_loc; |
| c_parser_consume_token (parser); |
| expr_loc = c_parser_peek_token (parser)->location; |
| lhsval = expr.value; |
| while (TREE_CODE (lhsval) == COMPOUND_EXPR) |
| lhsval = TREE_OPERAND (lhsval, 1); |
| if (DECL_P (lhsval) || handled_component_p (lhsval)) |
| mark_exp_read (lhsval); |
| next = c_parser_expr_no_commas (parser, NULL); |
| next = default_function_array_conversion (expr_loc, next); |
| expr.value = build_compound_expr (loc, expr.value, next.value); |
| expr.original_code = COMPOUND_EXPR; |
| expr.original_type = next.original_type; |
| } |
| return expr; |
| } |
| |
| /* Parse an expression and convert functions or arrays to |
| pointers. */ |
| |
| static struct c_expr |
| c_parser_expression_conv (c_parser *parser) |
| { |
| struct c_expr expr; |
| location_t loc = c_parser_peek_token (parser)->location; |
| expr = c_parser_expression (parser); |
| expr = default_function_array_conversion (loc, expr); |
| return expr; |
| } |
| |
| /* Parse a non-empty list of expressions. If CONVERT_P, convert |
| functions and arrays to pointers. If FOLD_P, fold the expressions. |
| |
| nonempty-expr-list: |
| assignment-expression |
| nonempty-expr-list , assignment-expression |
| */ |
| |
| static VEC(tree,gc) * |
| c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p, |
| VEC(tree,gc) **p_orig_types) |
| { |
| VEC(tree,gc) *ret; |
| VEC(tree,gc) *orig_types; |
| struct c_expr expr; |
| location_t loc = c_parser_peek_token (parser)->location; |
| |
| ret = make_tree_vector (); |
| if (p_orig_types == NULL) |
| orig_types = NULL; |
| else |
| orig_types = make_tree_vector (); |
| |
| expr = c_parser_expr_no_commas (parser, NULL); |
| if (convert_p) |
| expr = default_function_array_read_conversion (loc, expr); |
| if (fold_p) |
| expr.value = c_fully_fold (expr.value, false, NULL); |
| VEC_quick_push (tree, ret, expr.value); |
| if (orig_types != NULL) |
| VEC_quick_push (tree, orig_types, expr.original_type); |
| while (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| c_parser_consume_token (parser); |
| loc = c_parser_peek_token (parser)->location; |
| expr = c_parser_expr_no_commas (parser, NULL); |
| if (convert_p) |
| expr = default_function_array_read_conversion (loc, expr); |
| if (fold_p) |
| expr.value = c_fully_fold (expr.value, false, NULL); |
| VEC_safe_push (tree, gc, ret, expr.value); |
| if (orig_types != NULL) |
| VEC_safe_push (tree, gc, orig_types, expr.original_type); |
| } |
| if (orig_types != NULL) |
| *p_orig_types = orig_types; |
| return ret; |
| } |
| |
| /* Parse Objective-C-specific constructs. */ |
| |
| /* Parse an objc-class-definition. |
| |
| objc-class-definition: |
| @interface identifier objc-superclass[opt] objc-protocol-refs[opt] |
| objc-class-instance-variables[opt] objc-methodprotolist @end |
| @implementation identifier objc-superclass[opt] |
| objc-class-instance-variables[opt] |
| @interface identifier ( identifier ) objc-protocol-refs[opt] |
| objc-methodprotolist @end |
| @interface identifier ( ) objc-protocol-refs[opt] |
| objc-methodprotolist @end |
| @implementation identifier ( identifier ) |
| |
| objc-superclass: |
| : identifier |
| |
| "@interface identifier (" must start "@interface identifier ( |
| identifier ) ...": objc-methodprotolist in the first production may |
| not start with a parenthesized identifier as a declarator of a data |
| definition with no declaration specifiers if the objc-superclass, |
| objc-protocol-refs and objc-class-instance-variables are omitted. */ |
| |
| static void |
| c_parser_objc_class_definition (c_parser *parser, tree attributes) |
| { |
| bool iface_p; |
| tree id1; |
| tree superclass; |
| if (c_parser_next_token_is_keyword (parser, RID_AT_INTERFACE)) |
| iface_p = true; |
| else if (c_parser_next_token_is_keyword (parser, RID_AT_IMPLEMENTATION)) |
| iface_p = false; |
| else |
| gcc_unreachable (); |
| |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| return; |
| } |
| id1 = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| /* We have a category or class extension. */ |
| tree id2; |
| tree proto = NULL_TREE; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| if (iface_p && c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| { |
| /* We have a class extension. */ |
| id2 = NULL_TREE; |
| } |
| else |
| { |
| c_parser_error (parser, "expected identifier or %<)%>"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL); |
| return; |
| } |
| } |
| else |
| { |
| id2 = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| if (!iface_p) |
| { |
| objc_start_category_implementation (id1, id2); |
| return; |
| } |
| if (c_parser_next_token_is (parser, CPP_LESS)) |
| proto = c_parser_objc_protocol_refs (parser); |
| objc_start_category_interface (id1, id2, proto, attributes); |
| c_parser_objc_methodprotolist (parser); |
| c_parser_require_keyword (parser, RID_AT_END, "expected %<@end%>"); |
| objc_finish_interface (); |
| return; |
| } |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| return; |
| } |
| superclass = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| else |
| superclass = NULL_TREE; |
| if (iface_p) |
| { |
| tree proto = NULL_TREE; |
| if (c_parser_next_token_is (parser, CPP_LESS)) |
| proto = c_parser_objc_protocol_refs (parser); |
| objc_start_class_interface (id1, superclass, proto, attributes); |
| } |
| else |
| objc_start_class_implementation (id1, superclass); |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| c_parser_objc_class_instance_variables (parser); |
| if (iface_p) |
| { |
| objc_continue_interface (); |
| c_parser_objc_methodprotolist (parser); |
| c_parser_require_keyword (parser, RID_AT_END, "expected %<@end%>"); |
| objc_finish_interface (); |
| } |
| else |
| { |
| objc_continue_implementation (); |
| return; |
| } |
| } |
| |
| /* Parse objc-class-instance-variables. |
| |
| objc-class-instance-variables: |
| { objc-instance-variable-decl-list[opt] } |
| |
| objc-instance-variable-decl-list: |
| objc-visibility-spec |
| objc-instance-variable-decl ; |
| ; |
| objc-instance-variable-decl-list objc-visibility-spec |
| objc-instance-variable-decl-list objc-instance-variable-decl ; |
| objc-instance-variable-decl-list ; |
| |
| objc-visibility-spec: |
| @private |
| @protected |
| @public |
| |
| objc-instance-variable-decl: |
| struct-declaration |
| */ |
| |
| static void |
| c_parser_objc_class_instance_variables (c_parser *parser) |
| { |
| gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE)); |
| c_parser_consume_token (parser); |
| while (c_parser_next_token_is_not (parser, CPP_EOF)) |
| { |
| tree decls; |
| /* Parse any stray semicolon. */ |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, |
| "extra semicolon in struct or union specified"); |
| c_parser_consume_token (parser); |
| continue; |
| } |
| /* Stop if at the end of the instance variables. */ |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| c_parser_consume_token (parser); |
| break; |
| } |
| /* Parse any objc-visibility-spec. */ |
| if (c_parser_next_token_is_keyword (parser, RID_AT_PRIVATE)) |
| { |
| c_parser_consume_token (parser); |
| objc_set_visibility (OBJC_IVAR_VIS_PRIVATE); |
| continue; |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_AT_PROTECTED)) |
| { |
| c_parser_consume_token (parser); |
| objc_set_visibility (OBJC_IVAR_VIS_PROTECTED); |
| continue; |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_AT_PUBLIC)) |
| { |
| c_parser_consume_token (parser); |
| objc_set_visibility (OBJC_IVAR_VIS_PUBLIC); |
| continue; |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_AT_PACKAGE)) |
| { |
| c_parser_consume_token (parser); |
| objc_set_visibility (OBJC_IVAR_VIS_PACKAGE); |
| continue; |
| } |
| else if (c_parser_next_token_is (parser, CPP_PRAGMA)) |
| { |
| c_parser_pragma (parser, pragma_external); |
| continue; |
| } |
| |
| /* Parse some comma-separated declarations. */ |
| decls = c_parser_struct_declaration (parser); |
| { |
| /* Comma-separated instance variables are chained together in |
| reverse order; add them one by one. */ |
| tree ivar = nreverse (decls); |
| for (; ivar; ivar = DECL_CHAIN (ivar)) |
| objc_add_instance_variable (copy_node (ivar)); |
| } |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| } |
| |
| /* Parse an objc-class-declaration. |
| |
| objc-class-declaration: |
| @class identifier-list ; |
| */ |
| |
| static void |
| c_parser_objc_class_declaration (c_parser *parser) |
| { |
| tree list = NULL_TREE; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_CLASS)); |
| c_parser_consume_token (parser); |
| /* Any identifiers, including those declared as type names, are OK |
| here. */ |
| while (true) |
| { |
| tree id; |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| parser->error = false; |
| return; |
| } |
| id = c_parser_peek_token (parser)->value; |
| list = chainon (list, build_tree_list (NULL_TREE, id)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| objc_declare_class (list); |
| } |
| |
| /* Parse an objc-alias-declaration. |
| |
| objc-alias-declaration: |
| @compatibility_alias identifier identifier ; |
| */ |
| |
| static void |
| c_parser_objc_alias_declaration (c_parser *parser) |
| { |
| tree id1, id2; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_ALIAS)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| return; |
| } |
| id1 = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| return; |
| } |
| id2 = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| objc_declare_alias (id1, id2); |
| } |
| |
| /* Parse an objc-protocol-definition. |
| |
| objc-protocol-definition: |
| @protocol identifier objc-protocol-refs[opt] objc-methodprotolist @end |
| @protocol identifier-list ; |
| |
| "@protocol identifier ;" should be resolved as "@protocol |
| identifier-list ;": objc-methodprotolist may not start with a |
| semicolon in the first alternative if objc-protocol-refs are |
| omitted. */ |
| |
| static void |
| c_parser_objc_protocol_definition (c_parser *parser, tree attributes) |
| { |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_PROTOCOL)); |
| |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| return; |
| } |
| if (c_parser_peek_2nd_token (parser)->type == CPP_COMMA |
| || c_parser_peek_2nd_token (parser)->type == CPP_SEMICOLON) |
| { |
| tree list = NULL_TREE; |
| /* Any identifiers, including those declared as type names, are |
| OK here. */ |
| while (true) |
| { |
| tree id; |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| break; |
| } |
| id = c_parser_peek_token (parser)->value; |
| list = chainon (list, build_tree_list (NULL_TREE, id)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| objc_declare_protocols (list, attributes); |
| } |
| else |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| tree proto = NULL_TREE; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_LESS)) |
| proto = c_parser_objc_protocol_refs (parser); |
| parser->objc_pq_context = true; |
| objc_start_protocol (id, proto, attributes); |
| c_parser_objc_methodprotolist (parser); |
| c_parser_require_keyword (parser, RID_AT_END, "expected %<@end%>"); |
| parser->objc_pq_context = false; |
| objc_finish_interface (); |
| } |
| } |
| |
| /* Parse an objc-method-type. |
| |
| objc-method-type: |
| + |
| - |
| |
| Return true if it is a class method (+) and false if it is |
| an instance method (-). |
| */ |
| static inline bool |
| c_parser_objc_method_type (c_parser *parser) |
| { |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_PLUS: |
| c_parser_consume_token (parser); |
| return true; |
| case CPP_MINUS: |
| c_parser_consume_token (parser); |
| return false; |
| default: |
| gcc_unreachable (); |
| } |
| } |
| |
| /* Parse an objc-method-definition. |
| |
| objc-method-definition: |
| objc-method-type objc-method-decl ;[opt] compound-statement |
| */ |
| |
| static void |
| c_parser_objc_method_definition (c_parser *parser) |
| { |
| bool is_class_method = c_parser_objc_method_type (parser); |
| tree decl, attributes = NULL_TREE; |
| parser->objc_pq_context = true; |
| decl = c_parser_objc_method_decl (parser, is_class_method, &attributes); |
| if (decl == error_mark_node) |
| return; /* Bail here. */ |
| |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| c_parser_consume_token (parser); |
| pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, |
| "extra semicolon in method definition specified"); |
| } |
| |
| if (!c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| { |
| c_parser_error (parser, "expected %<{%>"); |
| return; |
| } |
| |
| parser->objc_pq_context = false; |
| if (objc_start_method_definition (is_class_method, decl, attributes)) |
| { |
| add_stmt (c_parser_compound_statement (parser)); |
| objc_finish_method_definition (current_function_decl); |
| } |
| else |
| { |
| /* This code is executed when we find a method definition |
| outside of an @implementation context (or invalid for other |
| reasons). Parse the method (to keep going) but do not emit |
| any code. |
| */ |
| c_parser_compound_statement (parser); |
| } |
| } |
| |
| /* Parse an objc-methodprotolist. |
| |
| objc-methodprotolist: |
| empty |
| objc-methodprotolist objc-methodproto |
| objc-methodprotolist declaration |
| objc-methodprotolist ; |
| @optional |
| @required |
| |
| The declaration is a data definition, which may be missing |
| declaration specifiers under the same rules and diagnostics as |
| other data definitions outside functions, and the stray semicolon |
| is diagnosed the same way as a stray semicolon outside a |
| function. */ |
| |
| static void |
| c_parser_objc_methodprotolist (c_parser *parser) |
| { |
| while (true) |
| { |
| /* The list is terminated by @end. */ |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_SEMICOLON: |
| pedwarn (c_parser_peek_token (parser)->location, OPT_pedantic, |
| "ISO C does not allow extra %<;%> outside of a function"); |
| c_parser_consume_token (parser); |
| break; |
| case CPP_PLUS: |
| case CPP_MINUS: |
| c_parser_objc_methodproto (parser); |
| break; |
| case CPP_PRAGMA: |
| c_parser_pragma (parser, pragma_external); |
| break; |
| case CPP_EOF: |
| return; |
| default: |
| if (c_parser_next_token_is_keyword (parser, RID_AT_END)) |
| return; |
| else if (c_parser_next_token_is_keyword (parser, RID_AT_PROPERTY)) |
| c_parser_objc_at_property_declaration (parser); |
| else if (c_parser_next_token_is_keyword (parser, RID_AT_OPTIONAL)) |
| { |
| objc_set_method_opt (true); |
| c_parser_consume_token (parser); |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_AT_REQUIRED)) |
| { |
| objc_set_method_opt (false); |
| c_parser_consume_token (parser); |
| } |
| else |
| c_parser_declaration_or_fndef (parser, false, false, true, |
| false, true, NULL); |
| break; |
| } |
| } |
| } |
| |
| /* Parse an objc-methodproto. |
| |
| objc-methodproto: |
| objc-method-type objc-method-decl ; |
| */ |
| |
| static void |
| c_parser_objc_methodproto (c_parser *parser) |
| { |
| bool is_class_method = c_parser_objc_method_type (parser); |
| tree decl, attributes = NULL_TREE; |
| |
| /* Remember protocol qualifiers in prototypes. */ |
| parser->objc_pq_context = true; |
| decl = c_parser_objc_method_decl (parser, is_class_method, &attributes); |
| /* Forget protocol qualifiers now. */ |
| parser->objc_pq_context = false; |
| |
| /* Do not allow the presence of attributes to hide an erroneous |
| method implementation in the interface section. */ |
| if (!c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| c_parser_error (parser, "expected %<;%>"); |
| return; |
| } |
| |
| if (decl != error_mark_node) |
| objc_add_method_declaration (is_class_method, decl, attributes); |
| |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| |
| /* If we are at a position that method attributes may be present, check that |
| there are not any parsed already (a syntax error) and then collect any |
| specified at the current location. Finally, if new attributes were present, |
| check that the next token is legal ( ';' for decls and '{' for defs). */ |
| |
| static bool |
| c_parser_objc_maybe_method_attributes (c_parser* parser, tree* attributes) |
| { |
| bool bad = false; |
| if (*attributes) |
| { |
| c_parser_error (parser, |
| "method attributes must be specified at the end only"); |
| *attributes = NULL_TREE; |
| bad = true; |
| } |
| |
| if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| *attributes = c_parser_attributes (parser); |
| |
| /* If there were no attributes here, just report any earlier error. */ |
| if (*attributes == NULL_TREE || bad) |
| return bad; |
| |
| /* If the attributes are followed by a ; or {, then just report any earlier |
| error. */ |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON) |
| || c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| return bad; |
| |
| /* We've got attributes, but not at the end. */ |
| c_parser_error (parser, |
| "expected %<;%> or %<{%> after method attribute definition"); |
| return true; |
| } |
| |
| /* Parse an objc-method-decl. |
| |
| objc-method-decl: |
| ( objc-type-name ) objc-selector |
| objc-selector |
| ( objc-type-name ) objc-keyword-selector objc-optparmlist |
| objc-keyword-selector objc-optparmlist |
| attributes |
| |
| objc-keyword-selector: |
| objc-keyword-decl |
| objc-keyword-selector objc-keyword-decl |
| |
| objc-keyword-decl: |
| objc-selector : ( objc-type-name ) identifier |
| objc-selector : identifier |
| : ( objc-type-name ) identifier |
| : identifier |
| |
| objc-optparmlist: |
| objc-optparms objc-optellipsis |
| |
| objc-optparms: |
| empty |
| objc-opt-parms , parameter-declaration |
| |
| objc-optellipsis: |
| empty |
| , ... |
| */ |
| |
| static tree |
| c_parser_objc_method_decl (c_parser *parser, bool is_class_method, tree *attributes) |
| { |
| tree type = NULL_TREE; |
| tree sel; |
| tree parms = NULL_TREE; |
| bool ellipsis = false; |
| bool attr_err = false; |
| |
| *attributes = NULL_TREE; |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| type = c_parser_objc_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| sel = c_parser_objc_selector (parser); |
| /* If there is no selector, or a colon follows, we have an |
| objc-keyword-selector. If there is a selector, and a colon does |
| not follow, that selector ends the objc-method-decl. */ |
| if (!sel || c_parser_next_token_is (parser, CPP_COLON)) |
| { |
| tree tsel = sel; |
| tree list = NULL_TREE; |
| while (true) |
| { |
| tree atype = NULL_TREE, id, keyworddecl; |
| tree param_attr = NULL_TREE; |
| if (!c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| break; |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| atype = c_parser_objc_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| } |
| /* New ObjC allows attributes on method parameters. */ |
| if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE)) |
| param_attr = c_parser_attributes (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| return error_mark_node; |
| } |
| id = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| keyworddecl = objc_build_keyword_decl (tsel, atype, id, param_attr); |
| list = chainon (list, keyworddecl); |
| tsel = c_parser_objc_selector (parser); |
| if (!tsel && c_parser_next_token_is_not (parser, CPP_COLON)) |
| break; |
| } |
| |
| attr_err |= c_parser_objc_maybe_method_attributes (parser, attributes) ; |
| |
| /* Parse the optional parameter list. Optional Objective-C |
| method parameters follow the C syntax, and may include '...' |
| to denote a variable number of arguments. */ |
| parms = make_node (TREE_LIST); |
| while (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| struct c_parm *parm; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_ELLIPSIS)) |
| { |
| ellipsis = true; |
| c_parser_consume_token (parser); |
| attr_err |= c_parser_objc_maybe_method_attributes |
| (parser, attributes) ; |
| break; |
| } |
| parm = c_parser_parameter_declaration (parser, NULL_TREE); |
| if (parm == NULL) |
| break; |
| parms = chainon (parms, |
| build_tree_list (NULL_TREE, grokparm (parm))); |
| } |
| sel = list; |
| } |
| else |
| attr_err |= c_parser_objc_maybe_method_attributes (parser, attributes) ; |
| |
| if (sel == NULL) |
| { |
| c_parser_error (parser, "objective-c method declaration is expected"); |
| return error_mark_node; |
| } |
| |
| if (attr_err) |
| return error_mark_node; |
| |
| return objc_build_method_signature (is_class_method, type, sel, parms, ellipsis); |
| } |
| |
| /* Parse an objc-type-name. |
| |
| objc-type-name: |
| objc-type-qualifiers[opt] type-name |
| objc-type-qualifiers[opt] |
| |
| objc-type-qualifiers: |
| objc-type-qualifier |
| objc-type-qualifiers objc-type-qualifier |
| |
| objc-type-qualifier: one of |
| in out inout bycopy byref oneway |
| */ |
| |
| static tree |
| c_parser_objc_type_name (c_parser *parser) |
| { |
| tree quals = NULL_TREE; |
| struct c_type_name *type_name = NULL; |
| tree type = NULL_TREE; |
| while (true) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| if (token->type == CPP_KEYWORD |
| && (token->keyword == RID_IN |
| || token->keyword == RID_OUT |
| || token->keyword == RID_INOUT |
| || token->keyword == RID_BYCOPY |
| || token->keyword == RID_BYREF |
| || token->keyword == RID_ONEWAY)) |
| { |
| quals = chainon (build_tree_list (NULL_TREE, token->value), quals); |
| c_parser_consume_token (parser); |
| } |
| else |
| break; |
| } |
| if (c_parser_next_tokens_start_typename (parser, cla_prefer_type)) |
| type_name = c_parser_type_name (parser); |
| if (type_name) |
| type = groktypename (type_name, NULL, NULL); |
| |
| /* If the type is unknown, and error has already been produced and |
| we need to recover from the error. In that case, use NULL_TREE |
| for the type, as if no type had been specified; this will use the |
| default type ('id') which is good for error recovery. */ |
| if (type == error_mark_node) |
| type = NULL_TREE; |
| |
| return build_tree_list (quals, type); |
| } |
| |
| /* Parse objc-protocol-refs. |
| |
| objc-protocol-refs: |
| < identifier-list > |
| */ |
| |
| static tree |
| c_parser_objc_protocol_refs (c_parser *parser) |
| { |
| tree list = NULL_TREE; |
| gcc_assert (c_parser_next_token_is (parser, CPP_LESS)); |
| c_parser_consume_token (parser); |
| /* Any identifiers, including those declared as type names, are OK |
| here. */ |
| while (true) |
| { |
| tree id; |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| break; |
| } |
| id = c_parser_peek_token (parser)->value; |
| list = chainon (list, build_tree_list (NULL_TREE, id)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| c_parser_require (parser, CPP_GREATER, "expected %<>%>"); |
| return list; |
| } |
| |
| /* Parse an objc-try-catch-finally-statement. |
| |
| objc-try-catch-finally-statement: |
| @try compound-statement objc-catch-list[opt] |
| @try compound-statement objc-catch-list[opt] @finally compound-statement |
| |
| objc-catch-list: |
| @catch ( objc-catch-parameter-declaration ) compound-statement |
| objc-catch-list @catch ( objc-catch-parameter-declaration ) compound-statement |
| |
| objc-catch-parameter-declaration: |
| parameter-declaration |
| '...' |
| |
| where '...' is to be interpreted literally, that is, it means CPP_ELLIPSIS. |
| |
| PS: This function is identical to cp_parser_objc_try_catch_finally_statement |
| for C++. Keep them in sync. */ |
| |
| static void |
| c_parser_objc_try_catch_finally_statement (c_parser *parser) |
| { |
| location_t location; |
| tree stmt; |
| |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_TRY)); |
| c_parser_consume_token (parser); |
| location = c_parser_peek_token (parser)->location; |
| objc_maybe_warn_exceptions (location); |
| stmt = c_parser_compound_statement (parser); |
| objc_begin_try_stmt (location, stmt); |
| |
| while (c_parser_next_token_is_keyword (parser, RID_AT_CATCH)) |
| { |
| struct c_parm *parm; |
| tree parameter_declaration = error_mark_node; |
| bool seen_open_paren = false; |
| |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| seen_open_paren = true; |
| if (c_parser_next_token_is (parser, CPP_ELLIPSIS)) |
| { |
| /* We have "@catch (...)" (where the '...' are literally |
| what is in the code). Skip the '...'. |
| parameter_declaration is set to NULL_TREE, and |
| objc_being_catch_clauses() knows that that means |
| '...'. */ |
| c_parser_consume_token (parser); |
| parameter_declaration = NULL_TREE; |
| } |
| else |
| { |
| /* We have "@catch (NSException *exception)" or something |
| like that. Parse the parameter declaration. */ |
| parm = c_parser_parameter_declaration (parser, NULL_TREE); |
| if (parm == NULL) |
| parameter_declaration = error_mark_node; |
| else |
| parameter_declaration = grokparm (parm); |
| } |
| if (seen_open_paren) |
| c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| else |
| { |
| /* If there was no open parenthesis, we are recovering from |
| an error, and we are trying to figure out what mistake |
| the user has made. */ |
| |
| /* If there is an immediate closing parenthesis, the user |
| probably forgot the opening one (ie, they typed "@catch |
| NSException *e)". Parse the closing parenthesis and keep |
| going. */ |
| if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| c_parser_consume_token (parser); |
| |
| /* If these is no immediate closing parenthesis, the user |
| probably doesn't know that parenthesis are required at |
| all (ie, they typed "@catch NSException *e"). So, just |
| forget about the closing parenthesis and keep going. */ |
| } |
| objc_begin_catch_clause (parameter_declaration); |
| if (c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) |
| c_parser_compound_statement_nostart (parser); |
| objc_finish_catch_clause (); |
| } |
| if (c_parser_next_token_is_keyword (parser, RID_AT_FINALLY)) |
| { |
| c_parser_consume_token (parser); |
| location = c_parser_peek_token (parser)->location; |
| stmt = c_parser_compound_statement (parser); |
| objc_build_finally_clause (location, stmt); |
| } |
| objc_finish_try_stmt (); |
| } |
| |
| /* Parse an objc-synchronized-statement. |
| |
| objc-synchronized-statement: |
| @synchronized ( expression ) compound-statement |
| */ |
| |
| static void |
| c_parser_objc_synchronized_statement (c_parser *parser) |
| { |
| location_t loc; |
| tree expr, stmt; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_SYNCHRONIZED)); |
| c_parser_consume_token (parser); |
| loc = c_parser_peek_token (parser)->location; |
| objc_maybe_warn_exceptions (loc); |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| expr = c_parser_expression (parser).value; |
| expr = c_fully_fold (expr, false, NULL); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| else |
| expr = error_mark_node; |
| stmt = c_parser_compound_statement (parser); |
| objc_build_synchronized (loc, expr, stmt); |
| } |
| |
| /* Parse an objc-selector; return NULL_TREE without an error if the |
| next token is not an objc-selector. |
| |
| objc-selector: |
| identifier |
| one of |
| enum struct union if else while do for switch case default |
| break continue return goto asm sizeof typeof __alignof |
| unsigned long const short volatile signed restrict _Complex |
| in out inout bycopy byref oneway int char float double void _Bool |
| |
| ??? Why this selection of keywords but not, for example, storage |
| class specifiers? */ |
| |
| static tree |
| c_parser_objc_selector (c_parser *parser) |
| { |
| c_token *token = c_parser_peek_token (parser); |
| tree value = token->value; |
| if (token->type == CPP_NAME) |
| { |
| c_parser_consume_token (parser); |
| return value; |
| } |
| if (token->type != CPP_KEYWORD) |
| return NULL_TREE; |
| switch (token->keyword) |
| { |
| case RID_ENUM: |
| case RID_STRUCT: |
| case RID_UNION: |
| case RID_IF: |
| case RID_ELSE: |
| case RID_WHILE: |
| case RID_DO: |
| case RID_FOR: |
| case RID_SWITCH: |
| case RID_CASE: |
| case RID_DEFAULT: |
| case RID_BREAK: |
| case RID_CONTINUE: |
| case RID_RETURN: |
| case RID_GOTO: |
| case RID_ASM: |
| case RID_SIZEOF: |
| case RID_TYPEOF: |
| case RID_ALIGNOF: |
| case RID_UNSIGNED: |
| case RID_LONG: |
| case RID_INT128: |
| case RID_CONST: |
| case RID_SHORT: |
| case RID_VOLATILE: |
| case RID_SIGNED: |
| case RID_RESTRICT: |
| case RID_COMPLEX: |
| case RID_IN: |
| case RID_OUT: |
| case RID_INOUT: |
| case RID_BYCOPY: |
| case RID_BYREF: |
| case RID_ONEWAY: |
| case RID_INT: |
| case RID_CHAR: |
| case RID_FLOAT: |
| case RID_DOUBLE: |
| case RID_VOID: |
| case RID_BOOL: |
| c_parser_consume_token (parser); |
| return value; |
| default: |
| return NULL_TREE; |
| } |
| } |
| |
| /* Parse an objc-selector-arg. |
| |
| objc-selector-arg: |
| objc-selector |
| objc-keywordname-list |
| |
| objc-keywordname-list: |
| objc-keywordname |
| objc-keywordname-list objc-keywordname |
| |
| objc-keywordname: |
| objc-selector : |
| : |
| */ |
| |
| static tree |
| c_parser_objc_selector_arg (c_parser *parser) |
| { |
| tree sel = c_parser_objc_selector (parser); |
| tree list = NULL_TREE; |
| if (sel && c_parser_next_token_is_not (parser, CPP_COLON)) |
| return sel; |
| while (true) |
| { |
| if (!c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| return list; |
| list = chainon (list, build_tree_list (sel, NULL_TREE)); |
| sel = c_parser_objc_selector (parser); |
| if (!sel && c_parser_next_token_is_not (parser, CPP_COLON)) |
| break; |
| } |
| return list; |
| } |
| |
| /* Parse an objc-receiver. |
| |
| objc-receiver: |
| expression |
| class-name |
| type-name |
| */ |
| |
| static tree |
| c_parser_objc_receiver (c_parser *parser) |
| { |
| if (c_parser_peek_token (parser)->type == CPP_NAME |
| && (c_parser_peek_token (parser)->id_kind == C_ID_TYPENAME |
| || c_parser_peek_token (parser)->id_kind == C_ID_CLASSNAME)) |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| return objc_get_class_reference (id); |
| } |
| return c_fully_fold (c_parser_expression (parser).value, false, NULL); |
| } |
| |
| /* Parse objc-message-args. |
| |
| objc-message-args: |
| objc-selector |
| objc-keywordarg-list |
| |
| objc-keywordarg-list: |
| objc-keywordarg |
| objc-keywordarg-list objc-keywordarg |
| |
| objc-keywordarg: |
| objc-selector : objc-keywordexpr |
| : objc-keywordexpr |
| */ |
| |
| static tree |
| c_parser_objc_message_args (c_parser *parser) |
| { |
| tree sel = c_parser_objc_selector (parser); |
| tree list = NULL_TREE; |
| if (sel && c_parser_next_token_is_not (parser, CPP_COLON)) |
| return sel; |
| while (true) |
| { |
| tree keywordexpr; |
| if (!c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| return error_mark_node; |
| keywordexpr = c_parser_objc_keywordexpr (parser); |
| list = chainon (list, build_tree_list (sel, keywordexpr)); |
| sel = c_parser_objc_selector (parser); |
| if (!sel && c_parser_next_token_is_not (parser, CPP_COLON)) |
| break; |
| } |
| return list; |
| } |
| |
| /* Parse an objc-keywordexpr. |
| |
| objc-keywordexpr: |
| nonempty-expr-list |
| */ |
| |
| static tree |
| c_parser_objc_keywordexpr (c_parser *parser) |
| { |
| tree ret; |
| VEC(tree,gc) *expr_list = c_parser_expr_list (parser, true, true, NULL); |
| if (VEC_length (tree, expr_list) == 1) |
| { |
| /* Just return the expression, remove a level of |
| indirection. */ |
| ret = VEC_index (tree, expr_list, 0); |
| } |
| else |
| { |
| /* We have a comma expression, we will collapse later. */ |
| ret = build_tree_list_vec (expr_list); |
| } |
| release_tree_vector (expr_list); |
| return ret; |
| } |
| |
| /* A check, needed in several places, that ObjC interface, implementation or |
| method definitions are not prefixed by incorrect items. */ |
| static bool |
| c_parser_objc_diagnose_bad_element_prefix (c_parser *parser, |
| struct c_declspecs *specs) |
| { |
| if (!specs->declspecs_seen_p || specs->non_sc_seen_p |
| || specs->typespec_kind != ctsk_none) |
| { |
| c_parser_error (parser, |
| "no type or storage class may be specified here,"); |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| return true; |
| } |
| return false; |
| } |
| |
| /* Parse an Objective-C @property declaration. The syntax is: |
| |
| objc-property-declaration: |
| '@property' objc-property-attributes[opt] struct-declaration ; |
| |
| objc-property-attributes: |
| '(' objc-property-attribute-list ')' |
| |
| objc-property-attribute-list: |
| objc-property-attribute |
| objc-property-attribute-list, objc-property-attribute |
| |
| objc-property-attribute |
| 'getter' = identifier |
| 'setter' = identifier |
| 'readonly' |
| 'readwrite' |
| 'assign' |
| 'retain' |
| 'copy' |
| 'nonatomic' |
| |
| For example: |
| @property NSString *name; |
| @property (readonly) id object; |
| @property (retain, nonatomic, getter=getTheName) id name; |
| @property int a, b, c; |
| |
| PS: This function is identical to cp_parser_objc_at_propery_declaration |
| for C++. Keep them in sync. */ |
| static void |
| c_parser_objc_at_property_declaration (c_parser *parser) |
| { |
| /* The following variables hold the attributes of the properties as |
| parsed. They are 'false' or 'NULL_TREE' if the attribute was not |
| seen. When we see an attribute, we set them to 'true' (if they |
| are boolean properties) or to the identifier (if they have an |
| argument, ie, for getter and setter). Note that here we only |
| parse the list of attributes, check the syntax and accumulate the |
| attributes that we find. objc_add_property_declaration() will |
| then process the information. */ |
| bool property_assign = false; |
| bool property_copy = false; |
| tree property_getter_ident = NULL_TREE; |
| bool property_nonatomic = false; |
| bool property_readonly = false; |
| bool property_readwrite = false; |
| bool property_retain = false; |
| tree property_setter_ident = NULL_TREE; |
| |
| /* 'properties' is the list of properties that we read. Usually a |
| single one, but maybe more (eg, in "@property int a, b, c;" there |
| are three). */ |
| tree properties; |
| location_t loc; |
| |
| loc = c_parser_peek_token (parser)->location; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_PROPERTY)); |
| |
| c_parser_consume_token (parser); /* Eat '@property'. */ |
| |
| /* Parse the optional attribute list... */ |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| /* Eat the '(' */ |
| c_parser_consume_token (parser); |
| |
| /* Property attribute keywords are valid now. */ |
| parser->objc_property_attr_context = true; |
| |
| while (true) |
| { |
| bool syntax_error = false; |
| c_token *token = c_parser_peek_token (parser); |
| enum rid keyword; |
| |
| if (token->type != CPP_KEYWORD) |
| { |
| if (token->type == CPP_CLOSE_PAREN) |
| c_parser_error (parser, "expected identifier"); |
| else |
| { |
| c_parser_consume_token (parser); |
| c_parser_error (parser, "unknown property attribute"); |
| } |
| break; |
| } |
| keyword = token->keyword; |
| c_parser_consume_token (parser); |
| switch (keyword) |
| { |
| case RID_ASSIGN: property_assign = true; break; |
| case RID_COPY: property_copy = true; break; |
| case RID_NONATOMIC: property_nonatomic = true; break; |
| case RID_READONLY: property_readonly = true; break; |
| case RID_READWRITE: property_readwrite = true; break; |
| case RID_RETAIN: property_retain = true; break; |
| |
| case RID_GETTER: |
| case RID_SETTER: |
| if (c_parser_next_token_is_not (parser, CPP_EQ)) |
| { |
| if (keyword == RID_GETTER) |
| c_parser_error (parser, |
| "missing %<=%> (after %<getter%> attribute)"); |
| else |
| c_parser_error (parser, |
| "missing %<=%> (after %<setter%> attribute)"); |
| syntax_error = true; |
| break; |
| } |
| c_parser_consume_token (parser); /* eat the = */ |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| syntax_error = true; |
| break; |
| } |
| if (keyword == RID_SETTER) |
| { |
| if (property_setter_ident != NULL_TREE) |
| c_parser_error (parser, "the %<setter%> attribute may only be specified once"); |
| else |
| property_setter_ident = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_COLON)) |
| c_parser_error (parser, "setter name must terminate with %<:%>"); |
| else |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| if (property_getter_ident != NULL_TREE) |
| c_parser_error (parser, "the %<getter%> attribute may only be specified once"); |
| else |
| property_getter_ident = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| break; |
| default: |
| c_parser_error (parser, "unknown property attribute"); |
| syntax_error = true; |
| break; |
| } |
| |
| if (syntax_error) |
| break; |
| |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| parser->objc_property_attr_context = false; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| /* ... and the property declaration(s). */ |
| properties = c_parser_struct_declaration (parser); |
| |
| if (properties == error_mark_node) |
| { |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| parser->error = false; |
| return; |
| } |
| |
| if (properties == NULL_TREE) |
| c_parser_error (parser, "expected identifier"); |
| else |
| { |
| /* Comma-separated properties are chained together in |
| reverse order; add them one by one. */ |
| properties = nreverse (properties); |
| |
| for (; properties; properties = TREE_CHAIN (properties)) |
| objc_add_property_declaration (loc, copy_node (properties), |
| property_readonly, property_readwrite, |
| property_assign, property_retain, |
| property_copy, property_nonatomic, |
| property_getter_ident, property_setter_ident); |
| } |
| |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| parser->error = false; |
| } |
| |
| /* Parse an Objective-C @synthesize declaration. The syntax is: |
| |
| objc-synthesize-declaration: |
| @synthesize objc-synthesize-identifier-list ; |
| |
| objc-synthesize-identifier-list: |
| objc-synthesize-identifier |
| objc-synthesize-identifier-list, objc-synthesize-identifier |
| |
| objc-synthesize-identifier |
| identifier |
| identifier = identifier |
| |
| For example: |
| @synthesize MyProperty; |
| @synthesize OneProperty, AnotherProperty=MyIvar, YetAnotherProperty; |
| |
| PS: This function is identical to cp_parser_objc_at_synthesize_declaration |
| for C++. Keep them in sync. |
| */ |
| static void |
| c_parser_objc_at_synthesize_declaration (c_parser *parser) |
| { |
| tree list = NULL_TREE; |
| location_t loc; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_SYNTHESIZE)); |
| loc = c_parser_peek_token (parser)->location; |
| |
| c_parser_consume_token (parser); |
| while (true) |
| { |
| tree property, ivar; |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| /* Once we find the semicolon, we can resume normal parsing. |
| We have to reset parser->error manually because |
| c_parser_skip_until_found() won't reset it for us if the |
| next token is precisely a semicolon. */ |
| parser->error = false; |
| return; |
| } |
| property = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_EQ)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| parser->error = false; |
| return; |
| } |
| ivar = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| else |
| ivar = NULL_TREE; |
| list = chainon (list, build_tree_list (ivar, property)); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| objc_add_synthesize_declaration (loc, list); |
| } |
| |
| /* Parse an Objective-C @dynamic declaration. The syntax is: |
| |
| objc-dynamic-declaration: |
| @dynamic identifier-list ; |
| |
| For example: |
| @dynamic MyProperty; |
| @dynamic MyProperty, AnotherProperty; |
| |
| PS: This function is identical to cp_parser_objc_at_dynamic_declaration |
| for C++. Keep them in sync. |
| */ |
| static void |
| c_parser_objc_at_dynamic_declaration (c_parser *parser) |
| { |
| tree list = NULL_TREE; |
| location_t loc; |
| gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_DYNAMIC)); |
| loc = c_parser_peek_token (parser)->location; |
| |
| c_parser_consume_token (parser); |
| while (true) |
| { |
| tree property; |
| if (c_parser_next_token_is_not (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected identifier"); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| parser->error = false; |
| return; |
| } |
| property = c_parser_peek_token (parser)->value; |
| list = chainon (list, build_tree_list (NULL_TREE, property)); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| objc_add_dynamic_declaration (loc, list); |
| } |
| |
| |
| /* Handle pragmas. Some OpenMP pragmas are associated with, and therefore |
| should be considered, statements. ALLOW_STMT is true if we're within |
| the context of a function and such pragmas are to be allowed. Returns |
| true if we actually parsed such a pragma. */ |
| |
| static bool |
| c_parser_pragma (c_parser *parser, enum pragma_context context) |
| { |
| unsigned int id; |
| |
| id = c_parser_peek_token (parser)->pragma_kind; |
| gcc_assert (id != PRAGMA_NONE); |
| |
| switch (id) |
| { |
| case PRAGMA_OMP_BARRIER: |
| if (context != pragma_compound) |
| { |
| if (context == pragma_stmt) |
| c_parser_error (parser, "%<#pragma omp barrier%> may only be " |
| "used in compound statements"); |
| goto bad_stmt; |
| } |
| c_parser_omp_barrier (parser); |
| return false; |
| |
| case PRAGMA_OMP_FLUSH: |
| if (context != pragma_compound) |
| { |
| if (context == pragma_stmt) |
| c_parser_error (parser, "%<#pragma omp flush%> may only be " |
| "used in compound statements"); |
| goto bad_stmt; |
| } |
| c_parser_omp_flush (parser); |
| return false; |
| |
| case PRAGMA_OMP_TASKWAIT: |
| if (context != pragma_compound) |
| { |
| if (context == pragma_stmt) |
| c_parser_error (parser, "%<#pragma omp taskwait%> may only be " |
| "used in compound statements"); |
| goto bad_stmt; |
| } |
| c_parser_omp_taskwait (parser); |
| return false; |
| |
| case PRAGMA_OMP_THREADPRIVATE: |
| c_parser_omp_threadprivate (parser); |
| return false; |
| |
| case PRAGMA_OMP_SECTION: |
| error_at (c_parser_peek_token (parser)->location, |
| "%<#pragma omp section%> may only be used in " |
| "%<#pragma omp sections%> construct"); |
| c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); |
| return false; |
| |
| case PRAGMA_GCC_PCH_PREPROCESS: |
| c_parser_error (parser, "%<#pragma GCC pch_preprocess%> must be first"); |
| c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); |
| return false; |
| |
| default: |
| if (id < PRAGMA_FIRST_EXTERNAL) |
| { |
| if (context == pragma_external) |
| { |
| bad_stmt: |
| c_parser_error (parser, "expected declaration specifiers"); |
| c_parser_skip_until_found (parser, CPP_PRAGMA_EOL, NULL); |
| return false; |
| } |
| c_parser_omp_construct (parser); |
| return true; |
| } |
| break; |
| } |
| |
| c_parser_consume_pragma (parser); |
| c_invoke_pragma_handler (id); |
| |
| /* Skip to EOL, but suppress any error message. Those will have been |
| generated by the handler routine through calling error, as opposed |
| to calling c_parser_error. */ |
| parser->error = true; |
| c_parser_skip_to_pragma_eol (parser); |
| |
| return false; |
| } |
| |
| /* The interface the pragma parsers have to the lexer. */ |
| |
| enum cpp_ttype |
| pragma_lex (tree *value) |
| { |
| c_token *tok = c_parser_peek_token (the_parser); |
| enum cpp_ttype ret = tok->type; |
| |
| *value = tok->value; |
| if (ret == CPP_PRAGMA_EOL || ret == CPP_EOF) |
| ret = CPP_EOF; |
| else |
| { |
| if (ret == CPP_KEYWORD) |
| ret = CPP_NAME; |
| c_parser_consume_token (the_parser); |
| } |
| |
| return ret; |
| } |
| |
| static void |
| c_parser_pragma_pch_preprocess (c_parser *parser) |
| { |
| tree name = NULL; |
| |
| c_parser_consume_pragma (parser); |
| if (c_parser_next_token_is (parser, CPP_STRING)) |
| { |
| name = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| } |
| else |
| c_parser_error (parser, "expected string literal"); |
| c_parser_skip_to_pragma_eol (parser); |
| |
| if (name) |
| c_common_pch_pragma (parse_in, TREE_STRING_POINTER (name)); |
| } |
| |
| /* OpenMP 2.5 parsing routines. */ |
| |
| /* Returns name of the next clause. |
| If the clause is not recognized PRAGMA_OMP_CLAUSE_NONE is returned and |
| the token is not consumed. Otherwise appropriate pragma_omp_clause is |
| returned and the token is consumed. */ |
| |
| static pragma_omp_clause |
| c_parser_omp_clause_name (c_parser *parser) |
| { |
| pragma_omp_clause result = PRAGMA_OMP_CLAUSE_NONE; |
| |
| if (c_parser_next_token_is_keyword (parser, RID_IF)) |
| result = PRAGMA_OMP_CLAUSE_IF; |
| else if (c_parser_next_token_is_keyword (parser, RID_DEFAULT)) |
| result = PRAGMA_OMP_CLAUSE_DEFAULT; |
| else if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); |
| |
| switch (p[0]) |
| { |
| case 'c': |
| if (!strcmp ("collapse", p)) |
| result = PRAGMA_OMP_CLAUSE_COLLAPSE; |
| else if (!strcmp ("copyin", p)) |
| result = PRAGMA_OMP_CLAUSE_COPYIN; |
| else if (!strcmp ("copyprivate", p)) |
| result = PRAGMA_OMP_CLAUSE_COPYPRIVATE; |
| break; |
| case 'f': |
| if (!strcmp ("firstprivate", p)) |
| result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE; |
| break; |
| case 'l': |
| if (!strcmp ("lastprivate", p)) |
| result = PRAGMA_OMP_CLAUSE_LASTPRIVATE; |
| break; |
| case 'n': |
| if (!strcmp ("nowait", p)) |
| result = PRAGMA_OMP_CLAUSE_NOWAIT; |
| else if (!strcmp ("num_threads", p)) |
| result = PRAGMA_OMP_CLAUSE_NUM_THREADS; |
| break; |
| case 'o': |
| if (!strcmp ("ordered", p)) |
| result = PRAGMA_OMP_CLAUSE_ORDERED; |
| break; |
| case 'p': |
| if (!strcmp ("private", p)) |
| result = PRAGMA_OMP_CLAUSE_PRIVATE; |
| break; |
| case 'r': |
| if (!strcmp ("reduction", p)) |
| result = PRAGMA_OMP_CLAUSE_REDUCTION; |
| break; |
| case 's': |
| if (!strcmp ("schedule", p)) |
| result = PRAGMA_OMP_CLAUSE_SCHEDULE; |
| else if (!strcmp ("shared", p)) |
| result = PRAGMA_OMP_CLAUSE_SHARED; |
| break; |
| case 'u': |
| if (!strcmp ("untied", p)) |
| result = PRAGMA_OMP_CLAUSE_UNTIED; |
| break; |
| } |
| } |
| |
| if (result != PRAGMA_OMP_CLAUSE_NONE) |
| c_parser_consume_token (parser); |
| |
| return result; |
| } |
| |
| /* Validate that a clause of the given type does not already exist. */ |
| |
| static void |
| check_no_duplicate_clause (tree clauses, enum omp_clause_code code, |
| const char *name) |
| { |
| tree c; |
| |
| for (c = clauses; c ; c = OMP_CLAUSE_CHAIN (c)) |
| if (OMP_CLAUSE_CODE (c) == code) |
| { |
| location_t loc = OMP_CLAUSE_LOCATION (c); |
| error_at (loc, "too many %qs clauses", name); |
| break; |
| } |
| } |
| |
| /* OpenMP 2.5: |
| variable-list: |
| identifier |
| variable-list , identifier |
| |
| If KIND is nonzero, create the appropriate node and install the |
| decl in OMP_CLAUSE_DECL and add the node to the head of the list. |
| If KIND is nonzero, CLAUSE_LOC is the location of the clause. |
| |
| If KIND is zero, create a TREE_LIST with the decl in TREE_PURPOSE; |
| return the list created. */ |
| |
| static tree |
| c_parser_omp_variable_list (c_parser *parser, |
| location_t clause_loc, |
| enum omp_clause_code kind, |
| tree list) |
| { |
| if (c_parser_next_token_is_not (parser, CPP_NAME) |
| || c_parser_peek_token (parser)->id_kind != C_ID_ID) |
| c_parser_error (parser, "expected identifier"); |
| |
| while (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_token (parser)->id_kind == C_ID_ID) |
| { |
| tree t = lookup_name (c_parser_peek_token (parser)->value); |
| |
| if (t == NULL_TREE) |
| undeclared_variable (c_parser_peek_token (parser)->location, |
| c_parser_peek_token (parser)->value); |
| else if (t == error_mark_node) |
| ; |
| else if (kind != 0) |
| { |
| tree u = build_omp_clause (clause_loc, kind); |
| OMP_CLAUSE_DECL (u) = t; |
| OMP_CLAUSE_CHAIN (u) = list; |
| list = u; |
| } |
| else |
| list = tree_cons (t, NULL_TREE, list); |
| |
| c_parser_consume_token (parser); |
| |
| if (c_parser_next_token_is_not (parser, CPP_COMMA)) |
| break; |
| |
| c_parser_consume_token (parser); |
| } |
| |
| return list; |
| } |
| |
| /* Similarly, but expect leading and trailing parenthesis. This is a very |
| common case for omp clauses. */ |
| |
| static tree |
| c_parser_omp_var_list_parens (c_parser *parser, enum omp_clause_code kind, |
| tree list) |
| { |
| /* The clauses location. */ |
| location_t loc = c_parser_peek_token (parser)->location; |
| |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| list = c_parser_omp_variable_list (parser, loc, kind, list); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| return list; |
| } |
| |
| /* OpenMP 3.0: |
| collapse ( constant-expression ) */ |
| |
| static tree |
| c_parser_omp_clause_collapse (c_parser *parser, tree list) |
| { |
| tree c, num = error_mark_node; |
| HOST_WIDE_INT n; |
| location_t loc; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_COLLAPSE, "collapse"); |
| |
| loc = c_parser_peek_token (parser)->location; |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| num = c_parser_expr_no_commas (parser, NULL).value; |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| if (num == error_mark_node) |
| return list; |
| if (!INTEGRAL_TYPE_P (TREE_TYPE (num)) |
| || !host_integerp (num, 0) |
| || (n = tree_low_cst (num, 0)) <= 0 |
| || (int) n != n) |
| { |
| error_at (loc, |
| "collapse argument needs positive constant integer expression"); |
| return list; |
| } |
| c = build_omp_clause (loc, OMP_CLAUSE_COLLAPSE); |
| OMP_CLAUSE_COLLAPSE_EXPR (c) = num; |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| copyin ( variable-list ) */ |
| |
| static tree |
| c_parser_omp_clause_copyin (c_parser *parser, tree list) |
| { |
| return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_COPYIN, list); |
| } |
| |
| /* OpenMP 2.5: |
| copyprivate ( variable-list ) */ |
| |
| static tree |
| c_parser_omp_clause_copyprivate (c_parser *parser, tree list) |
| { |
| return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_COPYPRIVATE, list); |
| } |
| |
| /* OpenMP 2.5: |
| default ( shared | none ) */ |
| |
| static tree |
| c_parser_omp_clause_default (c_parser *parser, tree list) |
| { |
| enum omp_clause_default_kind kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED; |
| location_t loc = c_parser_peek_token (parser)->location; |
| tree c; |
| |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return list; |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); |
| |
| switch (p[0]) |
| { |
| case 'n': |
| if (strcmp ("none", p) != 0) |
| goto invalid_kind; |
| kind = OMP_CLAUSE_DEFAULT_NONE; |
| break; |
| |
| case 's': |
| if (strcmp ("shared", p) != 0) |
| goto invalid_kind; |
| kind = OMP_CLAUSE_DEFAULT_SHARED; |
| break; |
| |
| default: |
| goto invalid_kind; |
| } |
| |
| c_parser_consume_token (parser); |
| } |
| else |
| { |
| invalid_kind: |
| c_parser_error (parser, "expected %<none%> or %<shared%>"); |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| |
| if (kind == OMP_CLAUSE_DEFAULT_UNSPECIFIED) |
| return list; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_DEFAULT, "default"); |
| c = build_omp_clause (loc, OMP_CLAUSE_DEFAULT); |
| OMP_CLAUSE_CHAIN (c) = list; |
| OMP_CLAUSE_DEFAULT_KIND (c) = kind; |
| |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| firstprivate ( variable-list ) */ |
| |
| static tree |
| c_parser_omp_clause_firstprivate (c_parser *parser, tree list) |
| { |
| return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_FIRSTPRIVATE, list); |
| } |
| |
| /* OpenMP 2.5: |
| if ( expression ) */ |
| |
| static tree |
| c_parser_omp_clause_if (c_parser *parser, tree list) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| tree t = c_parser_paren_condition (parser); |
| tree c; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_IF, "if"); |
| |
| c = build_omp_clause (loc, OMP_CLAUSE_IF); |
| OMP_CLAUSE_IF_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| list = c; |
| } |
| else |
| c_parser_error (parser, "expected %<(%>"); |
| |
| return list; |
| } |
| |
| /* OpenMP 2.5: |
| lastprivate ( variable-list ) */ |
| |
| static tree |
| c_parser_omp_clause_lastprivate (c_parser *parser, tree list) |
| { |
| return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_LASTPRIVATE, list); |
| } |
| |
| /* OpenMP 2.5: |
| nowait */ |
| |
| static tree |
| c_parser_omp_clause_nowait (c_parser *parser ATTRIBUTE_UNUSED, tree list) |
| { |
| tree c; |
| location_t loc = c_parser_peek_token (parser)->location; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_NOWAIT, "nowait"); |
| |
| c = build_omp_clause (loc, OMP_CLAUSE_NOWAIT); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| num_threads ( expression ) */ |
| |
| static tree |
| c_parser_omp_clause_num_threads (c_parser *parser, tree list) |
| { |
| location_t num_threads_loc = c_parser_peek_token (parser)->location; |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| location_t expr_loc = c_parser_peek_token (parser)->location; |
| tree c, t = c_parser_expression (parser).value; |
| t = c_fully_fold (t, false, NULL); |
| |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| |
| if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) |
| { |
| c_parser_error (parser, "expected integer expression"); |
| return list; |
| } |
| |
| /* Attempt to statically determine when the number isn't positive. */ |
| c = fold_build2_loc (expr_loc, LE_EXPR, boolean_type_node, t, |
| build_int_cst (TREE_TYPE (t), 0)); |
| if (CAN_HAVE_LOCATION_P (c)) |
| SET_EXPR_LOCATION (c, expr_loc); |
| if (c == boolean_true_node) |
| { |
| warning_at (expr_loc, 0, |
| "%<num_threads%> value must be positive"); |
| t = integer_one_node; |
| } |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_NUM_THREADS, "num_threads"); |
| |
| c = build_omp_clause (num_threads_loc, OMP_CLAUSE_NUM_THREADS); |
| OMP_CLAUSE_NUM_THREADS_EXPR (c) = t; |
| OMP_CLAUSE_CHAIN (c) = list; |
| list = c; |
| } |
| |
| return list; |
| } |
| |
| /* OpenMP 2.5: |
| ordered */ |
| |
| static tree |
| c_parser_omp_clause_ordered (c_parser *parser, tree list) |
| { |
| tree c; |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_ORDERED, "ordered"); |
| |
| c = build_omp_clause (c_parser_peek_token (parser)->location, |
| OMP_CLAUSE_ORDERED); |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* OpenMP 2.5: |
| private ( variable-list ) */ |
| |
| static tree |
| c_parser_omp_clause_private (c_parser *parser, tree list) |
| { |
| return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_PRIVATE, list); |
| } |
| |
| /* OpenMP 2.5: |
| reduction ( reduction-operator : variable-list ) |
| |
| reduction-operator: |
| One of: + * - & ^ | && || */ |
| |
| static tree |
| c_parser_omp_clause_reduction (c_parser *parser, tree list) |
| { |
| location_t clause_loc = c_parser_peek_token (parser)->location; |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| enum tree_code code; |
| |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_PLUS: |
| code = PLUS_EXPR; |
| break; |
| case CPP_MULT: |
| code = MULT_EXPR; |
| break; |
| case CPP_MINUS: |
| code = MINUS_EXPR; |
| break; |
| case CPP_AND: |
| code = BIT_AND_EXPR; |
| break; |
| case CPP_XOR: |
| code = BIT_XOR_EXPR; |
| break; |
| case CPP_OR: |
| code = BIT_IOR_EXPR; |
| break; |
| case CPP_AND_AND: |
| code = TRUTH_ANDIF_EXPR; |
| break; |
| case CPP_OR_OR: |
| code = TRUTH_ORIF_EXPR; |
| break; |
| default: |
| c_parser_error (parser, |
| "expected %<+%>, %<*%>, %<-%>, %<&%>, " |
| "%<^%>, %<|%>, %<&&%>, or %<||%>"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, 0); |
| return list; |
| } |
| c_parser_consume_token (parser); |
| if (c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| { |
| tree nl, c; |
| |
| nl = c_parser_omp_variable_list (parser, clause_loc, |
| OMP_CLAUSE_REDUCTION, list); |
| for (c = nl; c != list; c = OMP_CLAUSE_CHAIN (c)) |
| OMP_CLAUSE_REDUCTION_CODE (c) = code; |
| |
| list = nl; |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| return list; |
| } |
| |
| /* OpenMP 2.5: |
| schedule ( schedule-kind ) |
| schedule ( schedule-kind , expression ) |
| |
| schedule-kind: |
| static | dynamic | guided | runtime | auto |
| */ |
| |
| static tree |
| c_parser_omp_clause_schedule (c_parser *parser, tree list) |
| { |
| tree c, t; |
| location_t loc = c_parser_peek_token (parser)->location; |
| |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return list; |
| |
| c = build_omp_clause (loc, OMP_CLAUSE_SCHEDULE); |
| |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| tree kind = c_parser_peek_token (parser)->value; |
| const char *p = IDENTIFIER_POINTER (kind); |
| |
| switch (p[0]) |
| { |
| case 'd': |
| if (strcmp ("dynamic", p) != 0) |
| goto invalid_kind; |
| OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_DYNAMIC; |
| break; |
| |
| case 'g': |
| if (strcmp ("guided", p) != 0) |
| goto invalid_kind; |
| OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_GUIDED; |
| break; |
| |
| case 'r': |
| if (strcmp ("runtime", p) != 0) |
| goto invalid_kind; |
| OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_RUNTIME; |
| break; |
| |
| default: |
| goto invalid_kind; |
| } |
| } |
| else if (c_parser_next_token_is_keyword (parser, RID_STATIC)) |
| OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_STATIC; |
| else if (c_parser_next_token_is_keyword (parser, RID_AUTO)) |
| OMP_CLAUSE_SCHEDULE_KIND (c) = OMP_CLAUSE_SCHEDULE_AUTO; |
| else |
| goto invalid_kind; |
| |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| location_t here; |
| c_parser_consume_token (parser); |
| |
| here = c_parser_peek_token (parser)->location; |
| t = c_parser_expr_no_commas (parser, NULL).value; |
| t = c_fully_fold (t, false, NULL); |
| |
| if (OMP_CLAUSE_SCHEDULE_KIND (c) == OMP_CLAUSE_SCHEDULE_RUNTIME) |
| error_at (here, "schedule %<runtime%> does not take " |
| "a %<chunk_size%> parameter"); |
| else if (OMP_CLAUSE_SCHEDULE_KIND (c) == OMP_CLAUSE_SCHEDULE_AUTO) |
| error_at (here, |
| "schedule %<auto%> does not take " |
| "a %<chunk_size%> parameter"); |
| else if (TREE_CODE (TREE_TYPE (t)) == INTEGER_TYPE) |
| OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (c) = t; |
| else |
| c_parser_error (parser, "expected integer expression"); |
| |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| else |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<,%> or %<)%>"); |
| |
| check_no_duplicate_clause (list, OMP_CLAUSE_SCHEDULE, "schedule"); |
| OMP_CLAUSE_CHAIN (c) = list; |
| return c; |
| |
| invalid_kind: |
| c_parser_error (parser, "invalid schedule kind"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, 0); |
| return list; |
| } |
| |
| /* OpenMP 2.5: |
| shared ( variable-list ) */ |
| |
| static tree |
| c_parser_omp_clause_shared (c_parser *parser, tree list) |
| { |
| return c_parser_omp_var_list_parens (parser, OMP_CLAUSE_SHARED, list); |
| } |
| |
| /* OpenMP 3.0: |
| untied */ |
| |
| static tree |
| c_parser_omp_clause_untied (c_parser *parser ATTRIBUTE_UNUSED, tree list) |
| { |
| tree c; |
| |
| /* FIXME: Should we allow duplicates? */ |
| check_no_duplicate_clause (list, OMP_CLAUSE_UNTIED, "untied"); |
| |
| c = build_omp_clause (c_parser_peek_token (parser)->location, |
| OMP_CLAUSE_UNTIED); |
| OMP_CLAUSE_CHAIN (c) = list; |
| |
| return c; |
| } |
| |
| /* Parse all OpenMP clauses. The set clauses allowed by the directive |
| is a bitmask in MASK. Return the list of clauses found; the result |
| of clause default goes in *pdefault. */ |
| |
| static tree |
| c_parser_omp_all_clauses (c_parser *parser, unsigned int mask, |
| const char *where) |
| { |
| tree clauses = NULL; |
| bool first = true; |
| |
| while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) |
| { |
| location_t here; |
| pragma_omp_clause c_kind; |
| const char *c_name; |
| tree prev = clauses; |
| |
| if (!first && c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| |
| first = false; |
| here = c_parser_peek_token (parser)->location; |
| c_kind = c_parser_omp_clause_name (parser); |
| |
| switch (c_kind) |
| { |
| case PRAGMA_OMP_CLAUSE_COLLAPSE: |
| clauses = c_parser_omp_clause_collapse (parser, clauses); |
| c_name = "collapse"; |
| break; |
| case PRAGMA_OMP_CLAUSE_COPYIN: |
| clauses = c_parser_omp_clause_copyin (parser, clauses); |
| c_name = "copyin"; |
| break; |
| case PRAGMA_OMP_CLAUSE_COPYPRIVATE: |
| clauses = c_parser_omp_clause_copyprivate (parser, clauses); |
| c_name = "copyprivate"; |
| break; |
| case PRAGMA_OMP_CLAUSE_DEFAULT: |
| clauses = c_parser_omp_clause_default (parser, clauses); |
| c_name = "default"; |
| break; |
| case PRAGMA_OMP_CLAUSE_FIRSTPRIVATE: |
| clauses = c_parser_omp_clause_firstprivate (parser, clauses); |
| c_name = "firstprivate"; |
| break; |
| case PRAGMA_OMP_CLAUSE_IF: |
| clauses = c_parser_omp_clause_if (parser, clauses); |
| c_name = "if"; |
| break; |
| case PRAGMA_OMP_CLAUSE_LASTPRIVATE: |
| clauses = c_parser_omp_clause_lastprivate (parser, clauses); |
| c_name = "lastprivate"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NOWAIT: |
| clauses = c_parser_omp_clause_nowait (parser, clauses); |
| c_name = "nowait"; |
| break; |
| case PRAGMA_OMP_CLAUSE_NUM_THREADS: |
| clauses = c_parser_omp_clause_num_threads (parser, clauses); |
| c_name = "num_threads"; |
| break; |
| case PRAGMA_OMP_CLAUSE_ORDERED: |
| clauses = c_parser_omp_clause_ordered (parser, clauses); |
| c_name = "ordered"; |
| break; |
| case PRAGMA_OMP_CLAUSE_PRIVATE: |
| clauses = c_parser_omp_clause_private (parser, clauses); |
| c_name = "private"; |
| break; |
| case PRAGMA_OMP_CLAUSE_REDUCTION: |
| clauses = c_parser_omp_clause_reduction (parser, clauses); |
| c_name = "reduction"; |
| break; |
| case PRAGMA_OMP_CLAUSE_SCHEDULE: |
| clauses = c_parser_omp_clause_schedule (parser, clauses); |
| c_name = "schedule"; |
| break; |
| case PRAGMA_OMP_CLAUSE_SHARED: |
| clauses = c_parser_omp_clause_shared (parser, clauses); |
| c_name = "shared"; |
| break; |
| case PRAGMA_OMP_CLAUSE_UNTIED: |
| clauses = c_parser_omp_clause_untied (parser, clauses); |
| c_name = "untied"; |
| break; |
| default: |
| c_parser_error (parser, "expected %<#pragma omp%> clause"); |
| goto saw_error; |
| } |
| |
| if (((mask >> c_kind) & 1) == 0 && !parser->error) |
| { |
| /* Remove the invalid clause(s) from the list to avoid |
| confusing the rest of the compiler. */ |
| clauses = prev; |
| error_at (here, "%qs is not valid for %qs", c_name, where); |
| } |
| } |
| |
| saw_error: |
| c_parser_skip_to_pragma_eol (parser); |
| |
| return c_finish_omp_clauses (clauses); |
| } |
| |
| /* OpenMP 2.5: |
| structured-block: |
| statement |
| |
| In practice, we're also interested in adding the statement to an |
| outer node. So it is convenient if we work around the fact that |
| c_parser_statement calls add_stmt. */ |
| |
| static tree |
| c_parser_omp_structured_block (c_parser *parser) |
| { |
| tree stmt = push_stmt_list (); |
| c_parser_statement (parser); |
| return pop_stmt_list (stmt); |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp atomic new-line |
| expression-stmt |
| |
| expression-stmt: |
| x binop= expr | x++ | ++x | x-- | --x |
| binop: |
| +, *, -, /, &, ^, |, <<, >> |
| |
| where x is an lvalue expression with scalar type. |
| |
| LOC is the location of the #pragma token. */ |
| |
| static void |
| c_parser_omp_atomic (location_t loc, c_parser *parser) |
| { |
| tree lhs, rhs; |
| tree stmt; |
| enum tree_code code; |
| struct c_expr rhs_expr; |
| |
| c_parser_skip_to_pragma_eol (parser); |
| |
| lhs = c_parser_unary_expression (parser).value; |
| lhs = c_fully_fold (lhs, false, NULL); |
| switch (TREE_CODE (lhs)) |
| { |
| case ERROR_MARK: |
| saw_error: |
| c_parser_skip_to_end_of_block_or_statement (parser); |
| return; |
| |
| case PREINCREMENT_EXPR: |
| case POSTINCREMENT_EXPR: |
| lhs = TREE_OPERAND (lhs, 0); |
| code = PLUS_EXPR; |
| rhs = integer_one_node; |
| break; |
| |
| case PREDECREMENT_EXPR: |
| case POSTDECREMENT_EXPR: |
| lhs = TREE_OPERAND (lhs, 0); |
| code = MINUS_EXPR; |
| rhs = integer_one_node; |
| break; |
| |
| case COMPOUND_EXPR: |
| if (TREE_CODE (TREE_OPERAND (lhs, 0)) == SAVE_EXPR |
| && TREE_CODE (TREE_OPERAND (lhs, 1)) == COMPOUND_EXPR |
| && TREE_CODE (TREE_OPERAND (TREE_OPERAND (lhs, 1), 0)) == MODIFY_EXPR |
| && TREE_OPERAND (TREE_OPERAND (lhs, 1), 1) == TREE_OPERAND (lhs, 0) |
| && TREE_CODE (TREE_TYPE (TREE_OPERAND (TREE_OPERAND |
| (TREE_OPERAND (lhs, 1), 0), 0))) |
| == BOOLEAN_TYPE) |
| /* Undo effects of boolean_increment for post {in,de}crement. */ |
| lhs = TREE_OPERAND (TREE_OPERAND (lhs, 1), 0); |
| /* FALLTHRU */ |
| case MODIFY_EXPR: |
| if (TREE_CODE (lhs) == MODIFY_EXPR |
| && TREE_CODE (TREE_TYPE (TREE_OPERAND (lhs, 0))) == BOOLEAN_TYPE) |
| { |
| /* Undo effects of boolean_increment. */ |
| if (integer_onep (TREE_OPERAND (lhs, 1))) |
| { |
| /* This is pre or post increment. */ |
| rhs = TREE_OPERAND (lhs, 1); |
| lhs = TREE_OPERAND (lhs, 0); |
| code = NOP_EXPR; |
| break; |
| } |
| if (TREE_CODE (TREE_OPERAND (lhs, 1)) == TRUTH_NOT_EXPR |
| && TREE_OPERAND (lhs, 0) |
| == TREE_OPERAND (TREE_OPERAND (lhs, 1), 0)) |
| { |
| /* This is pre or post decrement. */ |
| rhs = TREE_OPERAND (lhs, 1); |
| lhs = TREE_OPERAND (lhs, 0); |
| code = NOP_EXPR; |
| break; |
| } |
| } |
| /* FALLTHRU */ |
| default: |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_MULT_EQ: |
| code = MULT_EXPR; |
| break; |
| case CPP_DIV_EQ: |
| code = TRUNC_DIV_EXPR; |
| break; |
| case CPP_PLUS_EQ: |
| code = PLUS_EXPR; |
| break; |
| case CPP_MINUS_EQ: |
| code = MINUS_EXPR; |
| break; |
| case CPP_LSHIFT_EQ: |
| code = LSHIFT_EXPR; |
| break; |
| case CPP_RSHIFT_EQ: |
| code = RSHIFT_EXPR; |
| break; |
| case CPP_AND_EQ: |
| code = BIT_AND_EXPR; |
| break; |
| case CPP_OR_EQ: |
| code = BIT_IOR_EXPR; |
| break; |
| case CPP_XOR_EQ: |
| code = BIT_XOR_EXPR; |
| break; |
| default: |
| c_parser_error (parser, |
| "invalid operator for %<#pragma omp atomic%>"); |
| goto saw_error; |
| } |
| |
| /* Arrange to pass the location of the assignment operator to |
| c_finish_omp_atomic. */ |
| loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| { |
| location_t rhs_loc = c_parser_peek_token (parser)->location; |
| rhs_expr = c_parser_expression (parser); |
| rhs_expr = default_function_array_read_conversion (rhs_loc, rhs_expr); |
| } |
| rhs = rhs_expr.value; |
| rhs = c_fully_fold (rhs, false, NULL); |
| break; |
| } |
| stmt = c_finish_omp_atomic (loc, code, lhs, rhs); |
| if (stmt != error_mark_node) |
| add_stmt (stmt); |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| |
| |
| /* OpenMP 2.5: |
| # pragma omp barrier new-line |
| */ |
| |
| static void |
| c_parser_omp_barrier (c_parser *parser) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_pragma (parser); |
| c_parser_skip_to_pragma_eol (parser); |
| |
| c_finish_omp_barrier (loc); |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp critical [(name)] new-line |
| structured-block |
| |
| LOC is the location of the #pragma itself. */ |
| |
| static tree |
| c_parser_omp_critical (location_t loc, c_parser *parser) |
| { |
| tree stmt, name = NULL; |
| |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| name = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| } |
| else |
| c_parser_error (parser, "expected identifier"); |
| } |
| else if (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) |
| c_parser_error (parser, "expected %<(%> or end of line"); |
| c_parser_skip_to_pragma_eol (parser); |
| |
| stmt = c_parser_omp_structured_block (parser); |
| return c_finish_omp_critical (loc, stmt, name); |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp flush flush-vars[opt] new-line |
| |
| flush-vars: |
| ( variable-list ) */ |
| |
| static void |
| c_parser_omp_flush (c_parser *parser) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_pragma (parser); |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| c_parser_omp_var_list_parens (parser, OMP_CLAUSE_ERROR, NULL); |
| else if (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) |
| c_parser_error (parser, "expected %<(%> or end of line"); |
| c_parser_skip_to_pragma_eol (parser); |
| |
| c_finish_omp_flush (loc); |
| } |
| |
| /* Parse the restricted form of the for statement allowed by OpenMP. |
| The real trick here is to determine the loop control variable early |
| so that we can push a new decl if necessary to make it private. |
| LOC is the location of the OMP in "#pragma omp". */ |
| |
| static tree |
| c_parser_omp_for_loop (location_t loc, |
| c_parser *parser, tree clauses, tree *par_clauses) |
| { |
| tree decl, cond, incr, save_break, save_cont, body, init, stmt, cl; |
| tree declv, condv, incrv, initv, ret = NULL; |
| bool fail = false, open_brace_parsed = false; |
| int i, collapse = 1, nbraces = 0; |
| location_t for_loc; |
| VEC(tree,gc) *for_block = make_tree_vector (); |
| |
| for (cl = clauses; cl; cl = OMP_CLAUSE_CHAIN (cl)) |
| if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_COLLAPSE) |
| collapse = tree_low_cst (OMP_CLAUSE_COLLAPSE_EXPR (cl), 0); |
| |
| gcc_assert (collapse >= 1); |
| |
| declv = make_tree_vec (collapse); |
| initv = make_tree_vec (collapse); |
| condv = make_tree_vec (collapse); |
| incrv = make_tree_vec (collapse); |
| |
| if (!c_parser_next_token_is_keyword (parser, RID_FOR)) |
| { |
| c_parser_error (parser, "for statement expected"); |
| return NULL; |
| } |
| for_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| |
| for (i = 0; i < collapse; i++) |
| { |
| int bracecount = 0; |
| |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| goto pop_scopes; |
| |
| /* Parse the initialization declaration or expression. */ |
| if (c_parser_next_tokens_start_declaration (parser)) |
| { |
| if (i > 0) |
| VEC_safe_push (tree, gc, for_block, c_begin_compound_stmt (true)); |
| c_parser_declaration_or_fndef (parser, true, true, true, true, true, NULL); |
| decl = check_for_loop_decls (for_loc, flag_isoc99); |
| if (decl == NULL) |
| goto error_init; |
| if (DECL_INITIAL (decl) == error_mark_node) |
| decl = error_mark_node; |
| init = decl; |
| } |
| else if (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_EQ) |
| { |
| struct c_expr decl_exp; |
| struct c_expr init_exp; |
| location_t init_loc; |
| |
| decl_exp = c_parser_postfix_expression (parser); |
| decl = decl_exp.value; |
| |
| c_parser_require (parser, CPP_EQ, "expected %<=%>"); |
| |
| init_loc = c_parser_peek_token (parser)->location; |
| init_exp = c_parser_expr_no_commas (parser, NULL); |
| init_exp = default_function_array_read_conversion (init_loc, |
| init_exp); |
| init = build_modify_expr (init_loc, decl, decl_exp.original_type, |
| NOP_EXPR, init_loc, init_exp.value, |
| init_exp.original_type); |
| init = c_process_expr_stmt (init_loc, init); |
| |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| } |
| else |
| { |
| error_init: |
| c_parser_error (parser, |
| "expected iteration declaration or initialization"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| fail = true; |
| goto parse_next; |
| } |
| |
| /* Parse the loop condition. */ |
| cond = NULL_TREE; |
| if (c_parser_next_token_is_not (parser, CPP_SEMICOLON)) |
| { |
| location_t cond_loc = c_parser_peek_token (parser)->location; |
| struct c_expr cond_expr = c_parser_binary_expression (parser, NULL); |
| |
| cond = cond_expr.value; |
| cond = c_objc_common_truthvalue_conversion (cond_loc, cond); |
| cond = c_fully_fold (cond, false, NULL); |
| switch (cond_expr.original_code) |
| { |
| case GT_EXPR: |
| case GE_EXPR: |
| case LT_EXPR: |
| case LE_EXPR: |
| break; |
| default: |
| /* Can't be cond = error_mark_node, because we want to preserve |
| the location until c_finish_omp_for. */ |
| cond = build1 (NOP_EXPR, boolean_type_node, error_mark_node); |
| break; |
| } |
| protected_set_expr_location (cond, cond_loc); |
| } |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>"); |
| |
| /* Parse the increment expression. */ |
| incr = NULL_TREE; |
| if (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)) |
| { |
| location_t incr_loc = c_parser_peek_token (parser)->location; |
| |
| incr = c_process_expr_stmt (incr_loc, |
| c_parser_expression (parser).value); |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| |
| if (decl == NULL || decl == error_mark_node || init == error_mark_node) |
| fail = true; |
| else |
| { |
| TREE_VEC_ELT (declv, i) = decl; |
| TREE_VEC_ELT (initv, i) = init; |
| TREE_VEC_ELT (condv, i) = cond; |
| TREE_VEC_ELT (incrv, i) = incr; |
| } |
| |
| parse_next: |
| if (i == collapse - 1) |
| break; |
| |
| /* FIXME: OpenMP 3.0 draft isn't very clear on what exactly is allowed |
| in between the collapsed for loops to be still considered perfectly |
| nested. Hopefully the final version clarifies this. |
| For now handle (multiple) {'s and empty statements. */ |
| do |
| { |
| if (c_parser_next_token_is_keyword (parser, RID_FOR)) |
| { |
| c_parser_consume_token (parser); |
| break; |
| } |
| else if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| { |
| c_parser_consume_token (parser); |
| bracecount++; |
| } |
| else if (bracecount |
| && c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| c_parser_consume_token (parser); |
| else |
| { |
| c_parser_error (parser, "not enough perfectly nested loops"); |
| if (bracecount) |
| { |
| open_brace_parsed = true; |
| bracecount--; |
| } |
| fail = true; |
| collapse = 0; |
| break; |
| } |
| } |
| while (1); |
| |
| nbraces += bracecount; |
| } |
| |
| save_break = c_break_label; |
| c_break_label = size_one_node; |
| save_cont = c_cont_label; |
| c_cont_label = NULL_TREE; |
| body = push_stmt_list (); |
| |
| if (open_brace_parsed) |
| { |
| location_t here = c_parser_peek_token (parser)->location; |
| stmt = c_begin_compound_stmt (true); |
| c_parser_compound_statement_nostart (parser); |
| add_stmt (c_end_compound_stmt (here, stmt, true)); |
| } |
| else |
| add_stmt (c_parser_c99_block_statement (parser)); |
| if (c_cont_label) |
| { |
| tree t = build1 (LABEL_EXPR, void_type_node, c_cont_label); |
| SET_EXPR_LOCATION (t, loc); |
| add_stmt (t); |
| } |
| |
| body = pop_stmt_list (body); |
| c_break_label = save_break; |
| c_cont_label = save_cont; |
| |
| while (nbraces) |
| { |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| c_parser_consume_token (parser); |
| nbraces--; |
| } |
| else if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| c_parser_consume_token (parser); |
| else |
| { |
| c_parser_error (parser, "collapsed loops not perfectly nested"); |
| while (nbraces) |
| { |
| location_t here = c_parser_peek_token (parser)->location; |
| stmt = c_begin_compound_stmt (true); |
| add_stmt (body); |
| c_parser_compound_statement_nostart (parser); |
| body = c_end_compound_stmt (here, stmt, true); |
| nbraces--; |
| } |
| goto pop_scopes; |
| } |
| } |
| |
| /* Only bother calling c_finish_omp_for if we haven't already generated |
| an error from the initialization parsing. */ |
| if (!fail) |
| { |
| stmt = c_finish_omp_for (loc, declv, initv, condv, incrv, body, NULL); |
| if (stmt) |
| { |
| if (par_clauses != NULL) |
| { |
| tree *c; |
| for (c = par_clauses; *c ; ) |
| if (OMP_CLAUSE_CODE (*c) != OMP_CLAUSE_FIRSTPRIVATE |
| && OMP_CLAUSE_CODE (*c) != OMP_CLAUSE_LASTPRIVATE) |
| c = &OMP_CLAUSE_CHAIN (*c); |
| else |
| { |
| for (i = 0; i < collapse; i++) |
| if (TREE_VEC_ELT (declv, i) == OMP_CLAUSE_DECL (*c)) |
| break; |
| if (i == collapse) |
| c = &OMP_CLAUSE_CHAIN (*c); |
| else if (OMP_CLAUSE_CODE (*c) == OMP_CLAUSE_FIRSTPRIVATE) |
| { |
| error_at (loc, |
| "iteration variable %qD should not be firstprivate", |
| OMP_CLAUSE_DECL (*c)); |
| *c = OMP_CLAUSE_CHAIN (*c); |
| } |
| else |
| { |
| /* Copy lastprivate (decl) clause to OMP_FOR_CLAUSES, |
| change it to shared (decl) in |
| OMP_PARALLEL_CLAUSES. */ |
| tree l = build_omp_clause (OMP_CLAUSE_LOCATION (*c), |
| OMP_CLAUSE_LASTPRIVATE); |
| OMP_CLAUSE_DECL (l) = OMP_CLAUSE_DECL (*c); |
| OMP_CLAUSE_CHAIN (l) = clauses; |
| clauses = l; |
| OMP_CLAUSE_SET_CODE (*c, OMP_CLAUSE_SHARED); |
| } |
| } |
| } |
| OMP_FOR_CLAUSES (stmt) = clauses; |
| } |
| ret = stmt; |
| } |
| pop_scopes: |
| while (!VEC_empty (tree, for_block)) |
| { |
| /* FIXME diagnostics: LOC below should be the actual location of |
| this particular for block. We need to build a list of |
| locations to go along with FOR_BLOCK. */ |
| stmt = c_end_compound_stmt (loc, VEC_pop (tree, for_block), true); |
| add_stmt (stmt); |
| } |
| release_tree_vector (for_block); |
| return ret; |
| } |
| |
| /* OpenMP 2.5: |
| #pragma omp for for-clause[optseq] new-line |
| for-loop |
| |
| LOC is the location of the #pragma token. |
| */ |
| |
| #define OMP_FOR_CLAUSE_MASK \ |
| ( (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (1u << PRAGMA_OMP_CLAUSE_ORDERED) \ |
| | (1u << PRAGMA_OMP_CLAUSE_SCHEDULE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_COLLAPSE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static tree |
| c_parser_omp_for (location_t loc, c_parser *parser) |
| { |
| tree block, clauses, ret; |
| |
| clauses = c_parser_omp_all_clauses (parser, OMP_FOR_CLAUSE_MASK, |
| "#pragma omp for"); |
| |
| block = c_begin_compound_stmt (true); |
| ret = c_parser_omp_for_loop (loc, parser, clauses, NULL); |
| block = c_end_compound_stmt (loc, block, true); |
| add_stmt (block); |
| |
| return ret; |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp master new-line |
| structured-block |
| |
| LOC is the location of the #pragma token. |
| */ |
| |
| static tree |
| c_parser_omp_master (location_t loc, c_parser *parser) |
| { |
| c_parser_skip_to_pragma_eol (parser); |
| return c_finish_omp_master (loc, c_parser_omp_structured_block (parser)); |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp ordered new-line |
| structured-block |
| |
| LOC is the location of the #pragma itself. |
| */ |
| |
| static tree |
| c_parser_omp_ordered (location_t loc, c_parser *parser) |
| { |
| c_parser_skip_to_pragma_eol (parser); |
| return c_finish_omp_ordered (loc, c_parser_omp_structured_block (parser)); |
| } |
| |
| /* OpenMP 2.5: |
| |
| section-scope: |
| { section-sequence } |
| |
| section-sequence: |
| section-directive[opt] structured-block |
| section-sequence section-directive structured-block |
| |
| SECTIONS_LOC is the location of the #pragma omp sections. */ |
| |
| static tree |
| c_parser_omp_sections_scope (location_t sections_loc, c_parser *parser) |
| { |
| tree stmt, substmt; |
| bool error_suppress = false; |
| location_t loc; |
| |
| loc = c_parser_peek_token (parser)->location; |
| if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) |
| { |
| /* Avoid skipping until the end of the block. */ |
| parser->error = false; |
| return NULL_TREE; |
| } |
| |
| stmt = push_stmt_list (); |
| |
| if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_SECTION) |
| { |
| substmt = push_stmt_list (); |
| |
| while (1) |
| { |
| c_parser_statement (parser); |
| |
| if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_SECTION) |
| break; |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| break; |
| if (c_parser_next_token_is (parser, CPP_EOF)) |
| break; |
| } |
| |
| substmt = pop_stmt_list (substmt); |
| substmt = build1 (OMP_SECTION, void_type_node, substmt); |
| SET_EXPR_LOCATION (substmt, loc); |
| add_stmt (substmt); |
| } |
| |
| while (1) |
| { |
| if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| break; |
| if (c_parser_next_token_is (parser, CPP_EOF)) |
| break; |
| |
| loc = c_parser_peek_token (parser)->location; |
| if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_SECTION) |
| { |
| c_parser_consume_pragma (parser); |
| c_parser_skip_to_pragma_eol (parser); |
| error_suppress = false; |
| } |
| else if (!error_suppress) |
| { |
| error_at (loc, "expected %<#pragma omp section%> or %<}%>"); |
| error_suppress = true; |
| } |
| |
| substmt = c_parser_omp_structured_block (parser); |
| substmt = build1 (OMP_SECTION, void_type_node, substmt); |
| SET_EXPR_LOCATION (substmt, loc); |
| add_stmt (substmt); |
| } |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, |
| "expected %<#pragma omp section%> or %<}%>"); |
| |
| substmt = pop_stmt_list (stmt); |
| |
| stmt = make_node (OMP_SECTIONS); |
| SET_EXPR_LOCATION (stmt, sections_loc); |
| TREE_TYPE (stmt) = void_type_node; |
| OMP_SECTIONS_BODY (stmt) = substmt; |
| |
| return add_stmt (stmt); |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp sections sections-clause[optseq] newline |
| sections-scope |
| |
| LOC is the location of the #pragma token. |
| */ |
| |
| #define OMP_SECTIONS_CLAUSE_MASK \ |
| ( (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_LASTPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (1u << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static tree |
| c_parser_omp_sections (location_t loc, c_parser *parser) |
| { |
| tree block, clauses, ret; |
| |
| clauses = c_parser_omp_all_clauses (parser, OMP_SECTIONS_CLAUSE_MASK, |
| "#pragma omp sections"); |
| |
| block = c_begin_compound_stmt (true); |
| ret = c_parser_omp_sections_scope (loc, parser); |
| if (ret) |
| OMP_SECTIONS_CLAUSES (ret) = clauses; |
| block = c_end_compound_stmt (loc, block, true); |
| add_stmt (block); |
| |
| return ret; |
| } |
| |
| /* OpenMP 2.5: |
| # pragma parallel parallel-clause new-line |
| # pragma parallel for parallel-for-clause new-line |
| # pragma parallel sections parallel-sections-clause new-line |
| |
| LOC is the location of the #pragma token. |
| */ |
| |
| #define OMP_PARALLEL_CLAUSE_MASK \ |
| ( (1u << PRAGMA_OMP_CLAUSE_IF) \ |
| | (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_DEFAULT) \ |
| | (1u << PRAGMA_OMP_CLAUSE_SHARED) \ |
| | (1u << PRAGMA_OMP_CLAUSE_COPYIN) \ |
| | (1u << PRAGMA_OMP_CLAUSE_REDUCTION) \ |
| | (1u << PRAGMA_OMP_CLAUSE_NUM_THREADS)) |
| |
| static tree |
| c_parser_omp_parallel (location_t loc, c_parser *parser) |
| { |
| enum pragma_kind p_kind = PRAGMA_OMP_PARALLEL; |
| const char *p_name = "#pragma omp parallel"; |
| tree stmt, clauses, par_clause, ws_clause, block; |
| unsigned int mask = OMP_PARALLEL_CLAUSE_MASK; |
| |
| if (c_parser_next_token_is_keyword (parser, RID_FOR)) |
| { |
| c_parser_consume_token (parser); |
| p_kind = PRAGMA_OMP_PARALLEL_FOR; |
| p_name = "#pragma omp parallel for"; |
| mask |= OMP_FOR_CLAUSE_MASK; |
| mask &= ~(1u << PRAGMA_OMP_CLAUSE_NOWAIT); |
| } |
| else if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| const char *p = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); |
| if (strcmp (p, "sections") == 0) |
| { |
| c_parser_consume_token (parser); |
| p_kind = PRAGMA_OMP_PARALLEL_SECTIONS; |
| p_name = "#pragma omp parallel sections"; |
| mask |= OMP_SECTIONS_CLAUSE_MASK; |
| mask &= ~(1u << PRAGMA_OMP_CLAUSE_NOWAIT); |
| } |
| } |
| |
| clauses = c_parser_omp_all_clauses (parser, mask, p_name); |
| |
| switch (p_kind) |
| { |
| case PRAGMA_OMP_PARALLEL: |
| block = c_begin_omp_parallel (); |
| c_parser_statement (parser); |
| stmt = c_finish_omp_parallel (loc, clauses, block); |
| break; |
| |
| case PRAGMA_OMP_PARALLEL_FOR: |
| block = c_begin_omp_parallel (); |
| c_split_parallel_clauses (loc, clauses, &par_clause, &ws_clause); |
| c_parser_omp_for_loop (loc, parser, ws_clause, &par_clause); |
| stmt = c_finish_omp_parallel (loc, par_clause, block); |
| OMP_PARALLEL_COMBINED (stmt) = 1; |
| break; |
| |
| case PRAGMA_OMP_PARALLEL_SECTIONS: |
| block = c_begin_omp_parallel (); |
| c_split_parallel_clauses (loc, clauses, &par_clause, &ws_clause); |
| stmt = c_parser_omp_sections_scope (loc, parser); |
| if (stmt) |
| OMP_SECTIONS_CLAUSES (stmt) = ws_clause; |
| stmt = c_finish_omp_parallel (loc, par_clause, block); |
| OMP_PARALLEL_COMBINED (stmt) = 1; |
| break; |
| |
| default: |
| gcc_unreachable (); |
| } |
| |
| return stmt; |
| } |
| |
| /* OpenMP 2.5: |
| # pragma omp single single-clause[optseq] new-line |
| structured-block |
| |
| LOC is the location of the #pragma. |
| */ |
| |
| #define OMP_SINGLE_CLAUSE_MASK \ |
| ( (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_COPYPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_NOWAIT)) |
| |
| static tree |
| c_parser_omp_single (location_t loc, c_parser *parser) |
| { |
| tree stmt = make_node (OMP_SINGLE); |
| SET_EXPR_LOCATION (stmt, loc); |
| TREE_TYPE (stmt) = void_type_node; |
| |
| OMP_SINGLE_CLAUSES (stmt) |
| = c_parser_omp_all_clauses (parser, OMP_SINGLE_CLAUSE_MASK, |
| "#pragma omp single"); |
| OMP_SINGLE_BODY (stmt) = c_parser_omp_structured_block (parser); |
| |
| return add_stmt (stmt); |
| } |
| |
| /* OpenMP 3.0: |
| # pragma omp task task-clause[optseq] new-line |
| |
| LOC is the location of the #pragma. |
| */ |
| |
| #define OMP_TASK_CLAUSE_MASK \ |
| ( (1u << PRAGMA_OMP_CLAUSE_IF) \ |
| | (1u << PRAGMA_OMP_CLAUSE_UNTIED) \ |
| | (1u << PRAGMA_OMP_CLAUSE_DEFAULT) \ |
| | (1u << PRAGMA_OMP_CLAUSE_PRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_FIRSTPRIVATE) \ |
| | (1u << PRAGMA_OMP_CLAUSE_SHARED)) |
| |
| static tree |
| c_parser_omp_task (location_t loc, c_parser *parser) |
| { |
| tree clauses, block; |
| |
| clauses = c_parser_omp_all_clauses (parser, OMP_TASK_CLAUSE_MASK, |
| "#pragma omp task"); |
| |
| block = c_begin_omp_task (); |
| c_parser_statement (parser); |
| return c_finish_omp_task (loc, clauses, block); |
| } |
| |
| /* OpenMP 3.0: |
| # pragma omp taskwait new-line |
| */ |
| |
| static void |
| c_parser_omp_taskwait (c_parser *parser) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_pragma (parser); |
| c_parser_skip_to_pragma_eol (parser); |
| |
| c_finish_omp_taskwait (loc); |
| } |
| |
| /* Main entry point to parsing most OpenMP pragmas. */ |
| |
| static void |
| c_parser_omp_construct (c_parser *parser) |
| { |
| enum pragma_kind p_kind; |
| location_t loc; |
| tree stmt; |
| |
| loc = c_parser_peek_token (parser)->location; |
| p_kind = c_parser_peek_token (parser)->pragma_kind; |
| c_parser_consume_pragma (parser); |
| |
| switch (p_kind) |
| { |
| case PRAGMA_OMP_ATOMIC: |
| c_parser_omp_atomic (loc, parser); |
| return; |
| case PRAGMA_OMP_CRITICAL: |
| stmt = c_parser_omp_critical (loc, parser); |
| break; |
| case PRAGMA_OMP_FOR: |
| stmt = c_parser_omp_for (loc, parser); |
| break; |
| case PRAGMA_OMP_MASTER: |
| stmt = c_parser_omp_master (loc, parser); |
| break; |
| case PRAGMA_OMP_ORDERED: |
| stmt = c_parser_omp_ordered (loc, parser); |
| break; |
| case PRAGMA_OMP_PARALLEL: |
| stmt = c_parser_omp_parallel (loc, parser); |
| break; |
| case PRAGMA_OMP_SECTIONS: |
| stmt = c_parser_omp_sections (loc, parser); |
| break; |
| case PRAGMA_OMP_SINGLE: |
| stmt = c_parser_omp_single (loc, parser); |
| break; |
| case PRAGMA_OMP_TASK: |
| stmt = c_parser_omp_task (loc, parser); |
| break; |
| default: |
| gcc_unreachable (); |
| } |
| |
| if (stmt) |
| gcc_assert (EXPR_LOCATION (stmt) != UNKNOWN_LOCATION); |
| } |
| |
| |
| /* OpenMP 2.5: |
| # pragma omp threadprivate (variable-list) */ |
| |
| static void |
| c_parser_omp_threadprivate (c_parser *parser) |
| { |
| tree vars, t; |
| location_t loc; |
| |
| c_parser_consume_pragma (parser); |
| loc = c_parser_peek_token (parser)->location; |
| vars = c_parser_omp_var_list_parens (parser, OMP_CLAUSE_ERROR, NULL); |
| |
| /* Mark every variable in VARS to be assigned thread local storage. */ |
| for (t = vars; t; t = TREE_CHAIN (t)) |
| { |
| tree v = TREE_PURPOSE (t); |
| |
| /* FIXME diagnostics: Ideally we should keep individual |
| locations for all the variables in the var list to make the |
| following errors more precise. Perhaps |
| c_parser_omp_var_list_parens() should construct a list of |
| locations to go along with the var list. */ |
| |
| /* If V had already been marked threadprivate, it doesn't matter |
| whether it had been used prior to this point. */ |
| if (TREE_CODE (v) != VAR_DECL) |
| error_at (loc, "%qD is not a variable", v); |
| else if (TREE_USED (v) && !C_DECL_THREADPRIVATE_P (v)) |
| error_at (loc, "%qE declared %<threadprivate%> after first use", v); |
| else if (! TREE_STATIC (v) && ! DECL_EXTERNAL (v)) |
| error_at (loc, "automatic variable %qE cannot be %<threadprivate%>", v); |
| else if (TREE_TYPE (v) == error_mark_node) |
| ; |
| else if (! COMPLETE_TYPE_P (TREE_TYPE (v))) |
| error_at (loc, "%<threadprivate%> %qE has incomplete type", v); |
| else |
| { |
| if (! DECL_THREAD_LOCAL_P (v)) |
| { |
| DECL_TLS_MODEL (v) = decl_default_tls_model (v); |
| /* If rtl has been already set for this var, call |
| make_decl_rtl once again, so that encode_section_info |
| has a chance to look at the new decl flags. */ |
| if (DECL_RTL_SET_P (v)) |
| make_decl_rtl (v); |
| } |
| C_DECL_THREADPRIVATE_P (v) = 1; |
| } |
| } |
| |
| c_parser_skip_to_pragma_eol (parser); |
| } |
| |
| |
| /* Parse a single source file. */ |
| |
| void |
| c_parse_file (void) |
| { |
| /* Use local storage to begin. If the first token is a pragma, parse it. |
| If it is #pragma GCC pch_preprocess, then this will load a PCH file |
| which will cause garbage collection. */ |
| c_parser tparser; |
| |
| memset (&tparser, 0, sizeof tparser); |
| the_parser = &tparser; |
| |
| if (c_parser_peek_token (&tparser)->pragma_kind == PRAGMA_GCC_PCH_PREPROCESS) |
| c_parser_pragma_pch_preprocess (&tparser); |
| |
| the_parser = ggc_alloc_c_parser (); |
| *the_parser = tparser; |
| |
| /* Initialize EH, if we've been told to do so. */ |
| if (flag_exceptions) |
| using_eh_for_cleanups (); |
| |
| c_parser_translation_unit (the_parser); |
| the_parser = NULL; |
| } |
| |
| #include "gt-c-parser.h" |